C语言总结

目录

enum和struct是一样类型的吗

结构体和共用体精简总结

全局变量和局部变量在内存中是否有区别?如果有,是什么区别?

内存分区:

堆栈

堆栈区别联系

位运算基本介绍

static 的用法

extern 关键字的作用

sizeof 的用法

什么是预编译(define)?

# 和 ## 的作用?

sizeof运算符 和函数strlen的区别与用法

字符输入和输出

字符比较

字符串处理

字符转换

getchar和scanf有什么区别putchar和printf有什么区别

结构体的赋值

malloc/free 与new/delete 的区别

C++里的类 和 C里面的 struct 区别

主函数的写法

printf();

操作符优先级问题

逻辑短路问题 : 有逻辑运算参与    

分支

   将一维数组中的元素分别存入二维数组中

strcpy() 和memcpy()的区别?

指针

不定长函数参数

const    不可改变的变量

题14:大小端问题

静态局部变量在什么时候分配内存

#define宏定义与typedef的区别

值传递改变值这类问题

计算sizeof的值



  • 基本类型:整型(signed、unsigned)、字符型(char)、实型(float、double)、枚举型(enum)、数组、结构体(struct)、共用体(union)
  • 构造类型:指针类型、空类型(void)
  • 数据类型:短(short)、长(int、long、long long)

整型(int)

对于无符号类型,64位系统则使用4字节存储。

对于有符号类型,64位系统则使用8字节存储。

字符型(char)

对于单字节字符集(如ASCII码),1个字符通常占用1字节。

对于多字节字符集(如UTF-8编码),1个字符可能占用多个字节。

浮点数(float)

单精度浮点数(float)占用4字节

双精度浮点数(double)占用8字节。

float :单精度浮点型,存储4字节 ,打印格式为%f 。
double :双精度浮点型,存储8字节,打印格式为%lf 。

printf("%.2f\n", number);想要将一个浮点数格式化为小数点后两位:

指针

指针占4个字节。

int整型和long长整型

占用4个字节

指针类型

是一个特殊的变量,他里面存储的数值被解释为内存的地址。

bool类型

只有两个量true和false,表示逻辑真值和逻辑假值。

1 B/byte(字节) = 8 bit(比特)(位)

1 KB(千字节) = 1024 B/byte(字节)

1 MB = 1024 KB

1 GB = 1024 MB

1TB =1024 GB

1 PB = 1024 TB

1 EB = 1024 PB

enum和struct是一样类型的吗

enum(枚举)和 struct(结构体)在 C 语言中是两种不同的类型。

  • 枚举(enum) 是一种用户定义的数据类型,它由一组命名的整数常量组成。枚举主要用于定义一组相关的整数值,使得代码更加可读和维护。

  • 结构体(struct) 是一种复合数据类型,它允许你将不同的变量组合在一起作为一个单一的实体。结构体可以包含不同类型的数据项,这些数据项被称为结构体的成员。

结构体和共用体精简总结

  • 结构体(struct)

    • 独立存储:每个成员有自己的存储空间。
    • 同时存储:可以同时存储所有成员的值。
    • 大小:所有成员大小之和,可能包含填充和对齐。
  • 共用体(union)

    • 共享存储:所有成员共享同一块内存空间。
    • 单一存储:任意时刻只能存储一个成员的值。
    • 大小:最大成员的大小。

int p; //这是一个普通的整型变量
int *p; //首先从P 处开始,先与*结合,所以说明P 是一个指针,然后再与int 结合,说明指针所指向的内容的类型为int 型.所以P是一个返回整型数据的指针
int p[3]; //首先从P 处开始,先与[]结合,说明P 是一个数组,然后与int 结合,说明数组里的元素是整型的,所以P 是一个由整型数据组成的数组
int *p[3]; //首先从P 处开始,先与[]结合,因为其优先级比*高,所以P 是一个数组,然后再与*结合,说明数组里的元素是指针类型,然后再与int 结合,说明指针所指向的内容的类型是整型的,所以P 是一个由返回整型数据的指针所组成的数组
int (*p)[3]; //首先从P 处开始,先与*结合,说明P 是一个指针然后再与[]结合(与"()"这步可以忽略,只是为了改变优先级),说明指针所指向的内容是一个数组,然后再与int 结合,说明数组里的元素是整型的.所以P 是一个指向由整型数据组成的数组的指针
int **p; //首先从P 开始,先与*结合,说是P 是一个指针,然后再与*结合,说明指针所指向的元素是指针,然后再与int 结合,说明该指针所指向的元素是整型数据.由于二级指针以及更高级的指针极少用在复杂的类型中,所以后面更复杂的类型我们就不考虑多级指针了,最多只考虑一级指针.
int p(int); //从P 处起,先与()结合,说明P 是一个函数,然后进入()里分析,说明该函数有一个整型变量的参数,然后再与外面的int 结合,说明函数的返回值是一个整型数据
Int (*p)(int); //从P 处开始,先与指针结合,说明P 是一个指针,然后与()结合,说明指针指向的是一个函数,然后再与()里的int 结合,说明函数有一个int 型的参数,再与最外层的int 结合,说明函数的返回类型是整型,所以P 是一个指向有一个整型参数且返回类型为整型的函数的指针
int *(*p(int))[3]; //可以先跳过,不看这个类型,过于复杂从P 开始,先与()结合,说明P 是一个函数,然后进入()里面,与int 结合,说明函数有一个整型变量参数,然后再与外面的*结合,说明函数返回的是一个指针,,然后到最外面一层,先与[]结合,说明返回的指针指向的是一个数组,然后再与*结合,说明数组里的元素是指针,然后再与int 结合,说明指针指向的内容是整型数据.所以P 是一个参数为一个整数据且返回一个指向由整型指针变量组成的数组的指针变量的函数.

(1)int*ptr; //指针所指向的类型是int
(2)char*ptr; //指针所指向的的类型是char
(3)int**ptr; //指针所指向的的类型是int*
(4)int(*ptr)[3]; //指针所指向的的类型是int()[3]
(5)int*(*ptr)[4]; //指针所指向的的类型是int*()[4]

指针函数和函数指针

  • 指针函数是返回指针的函数。指针函数通常用于需要返回内存地址的场景,例如动态分配内存。
  • 函数指针是指向函数的指针。函数指针用于调用不同的函数,或者将函数作为参数传递给其他函数。

指针函数        int* createArray(int size)

函数指针        void process(int value, int (*funcPtr)(int))

指针数组和数组指针

指针数组是一个数组,其元素都是指针。这意味着每个元素都存储了一个地址,这个地址可以是任何类型的变量的地址。

int *intPtrArray[10];

数组指针是一个指向数组的指针。这意味着这个指针的值是一个数组的地址。数组指针通常用来指向多维数组或用于参数传递。

int (*intArrayPtr)[10];

  • 指针数组是一个数组,其元素是指针。
  • 数组指针是一个指针,它指向一个数组。
  • 指针数组常用于存储和访问多个不同变量的地址。
  • 数组指针常用于在函数参数中传递整个数组,而不需要知道数组的具体大小。
#include <stdio.h>

int main() {
    int var = 10;
    int *ptr = &var;  // ptr 现在持有 var 的地址

    printf("Value of var: %d\n", var);       // 输出 10
    printf("Address stored in ptr: %p\n", ptr);  // 输出 var 的地址
    printf("Value pointed to by ptr: %d\n", *ptr);  // 输出 10

    *ptr = 20;  // 通过指针修改 var 的值
    printf("New value of var: %d\n", var);  // 输出 20

    return 0;
}

在这个例子中,ptr 是一个指向整型变量 var 的指针。通过指针,我们可以获取和修改 var 的值。指针是 C 和 C++ 编程中的核心概念,掌握它们对于深入理解这些语言至关重要。

全局变量和局部变量在内存中是否有区别?如果有,是什么区别?

全局变量存储在静态数据区,而局部变量存储在栈区。

全局变量在程序运行期间始终存在,其内存空间在程序启动时分配,在程序结束时释放。局部变量则在函数被调用时创建,在函数执行结束时销毁,其内存空间随着函数的执行而动态分配与释放。

全局变量:在函数外面  作用于整个周期,

  • 在程序运行期间,全局变量会一直占据内存空间。
  • 如果在函数内部修改了全局变量,则这个修改在函数外部也是可见的。

局部变量:在函数里面,在局部范围内

  • 局部变量在函数调用时创建,函数调用结束后局部变量就会被销毁。
  • 局部变量只能在其作用域内部被访问和修改。

内存分区:

        栈区:函数参数 局部变量 系统管理(释放 回收)
        
        堆区:程序员管理(malloc/free)作用域 生命周期

        *静态存储区:全局变量 静态数据(static) 常量
            bss段:未初始化的全局变量、静态变量
            data段:初始化全局变量  静态变量  常量
        
        代码区:程序代码

堆栈

首先是栈,它是一种后进先出(LIFO)的数据结构,在计算机中具有重要的作用。

栈是一种高效的内存结构,用于存放基础数据类型和引用类型的变量,大大简化内存的管理,提高了程序的执行效率。

程序在运行时,会将栈用来存储函数的调用栈内存的分配操作表达式求值的临时变量以及与程序中的控制流相关的数据

堆内存则是用于分配程序中动态数据结构的内存空间,它的生命周期不由程序的函数调用栈管理,因此堆空间通常会被程序员直接管理

堆空间则为程序提供了极为灵活的空间分配和管理手段,既可以手动管理,也可以交由垃圾回收机制自动管理,带来了很大的方便。

堆栈区别联系

首先是区别:

    堆与栈的管理方式不同是由系统自动分配和管理的,而需要程序员手动进行分配和管理
    堆和栈的内存大小不同栈的大小是固定的,由操作系统指定,而堆空间通常由系统分配初始大小,程序员可以自由地调整大小。
    堆和栈的数据存储方式不同;栈空间用于存储基本数据类型和引用类型的地址,堆空间则用于存储动态分配的对象和复杂数据结构。

    其次是联系:

    堆和栈都是内存区域,是程序中存储数据和指令的关键组成部分。
    堆和栈都是用于存储变量和对象的内存空间。
    程序在运行时,常会使用堆和栈的组合来实现内存分配和管理。

位运算基本介绍

与运算:&

​ 同1则1,否则为0;

或运算:|

​ 同0为0,否则为1

非运算:~

​ 1取0 0 取1

异或运算:^

​ 两者相等为0,不等为1

static 的用法

在 C 和 C++ 中,static 关键字用于声明全局变量、局部变量、函数和类成员。

  1. 全局变量

    • static 用于声明全局变量时,表示该变量仅在当前文件中可见,即文件作用域。
  2. 局部变量

    • 在函数内部,static 声明的局部变量在函数调用之间保持其值,即其生命周期延长到程序的整个运行期间。
  3. 函数

    • 在 C++ 中,static 用于声明类成员函数时,表示该函数是静态的,它不依赖于类的实例,因此不能直接访问非静态成员。
  4. 类成员

    • 在 C++ 中,static 用于声明类成员变量时,表示该变量是静态的,所有类的实例共享同一个变量。

extern 关键字的作用

        用来修饰变量或函数,表明该变量或函数都是在别的文件中定义的,提醒编译器在其他文件中寻找定义

sizeof 的用法

        计算对象或类型所占的内存字节数

        sizeof 求得的结构体(及其对象)的大小,不等于各个数据成员大小之和(地址对齐问题)

什么是预编译(define)?

        预编译又称预处理,是整个编译最先做的工作,及程序执行前的一些预处理工作

        主要处理开头的指令,如拷贝  #include包含的文件代码、替换 #define定义的宏、条件编译等

        何时需要预编译? 总是使用并不经常改动的大型代码体

# 和 ## 的作用?

        #是宏参数 字符串替换运算符,## 是把两个 宏参数 连接起来的运算符

        #define   STR(arg)        #arg        //STR(hello)     ---> hello

        #define  NAME(x,y)       x##y        //NAME(1,2)    ---->12

sizeof运算符 和函数strlen的区别与用法

字符输入和输出

  • 输入

    • 使用 getchar() 从标准输入读取一个字符。
    • 使用 scanf() 配合 %c 格式字符串从标准输入读取一个字符。
  • 输出

    • 使用 putchar() 将一个字符写入标准输出。
    • 使用 printf() 配合 %c 格式字符串输出一个字符。

字符比较

  • 使用 strcmp() 比较两个字符串,返回 0 表示相等。
  • 使用 strcmp() 比较两个字符串,返回非零值表示不等。

字符串处理

  • 使用 strlen() 计算字符串长度。
  • 使用 strcpy()strncpy() 复制字符串。
  • 使用 strcat() 连接字符串。
  • 使用 strchr() 查找字符串中的字符。
  • 使用 strstr() 查找字符串中的子字符串。

字符转换

  • 使用 toupper()tolower() 转换字符为大写和小写。
  • 使用 atoi() 将字符串转换为整数。
  • 使用 strtol()strtoul() 将字符串转换为长整型。
  • 使用 strtod()strtold() 将字符串转换为双精度浮点数

getchar和scanf有什么区别putchar和printf有什么区别

  • getchar()scanf() 用于从标准输入读取字符或字符串,但 scanf() 更强大,因为它能够解析输入,而 getchar() 则不解析。
  • putchar()printf() 用于向标准输出输出字符或字符串,但 printf() 更强大,因为它支持多种格式化输出。

结构体的赋值

当你定义一个结构体变量时,可以直接初始化它:

struct Person {
    char name[50];
    int age;
    float height;
};

struct Person person1 = {"John Doe", 30, 5.9f};

malloc/free 与new/delete 的区别

1.malloc/free 是C/C++语言的标准库函数,new/delete 是C++的运算符,都是动态分配和释放内存。

2.主要区别就是 new/delete 对象在创建和释放的时候会执行构造函数和析构函数

C++里的类 和 C里面的 struct 区别

        C++   中的类具有成员保护功能,并且具有继承和多态的特点

        C里面没有成员函数,可以有函数指针,不能继承和派生,但可以结构体嵌套

主函数的写法

        int main(void)  {}

        int  main( int argc, char  *args[] ){}

        argc  运行程序执行的参数个数 , 第一个是程序本身   xx.out

        args   指向字符串数组的指针   xx.out  ...

printf();

        int   a=10, b=20, c=30;

        printf("%d  %d   %d\n",a+b+c, (b=b*2), (c=c*2) );  //  110  40  60   输出从右往左

        float   double 的输出   %.2f  --- 输出两位小数,会自动进行四舍五入

                     %8.2f --- 8是输出宽度,就是数据输出后在终端上占据8个 英文字母,不足的补空格

                float  a= 13.141592f;

                printf("%d\n", a );    //输出随机数,从内存里随机取来输出,不进行类型转换

操作符优先级问题

        char  *ptr  = "Linux";        //在内存中占了 6个字节 '\0'

        printf( "%c\n", *ptr++ );   // L 同一级,自右向左      ++*ptr ==M 编译报错,L常量不允许修改

        printf("%c\n", *ptr);         // i

        书写时多加(),

        伪运算符 优先级最高()

        单目大于双目  三目

        算符运算符 >  比较运算符 > 位运算符  > 逻辑运算符

自右向左:只有单目(++ --)  三目  赋值运算符

        三目运算: a?b:c?d:e  就是  a?b : (c?d:e )     //运算从左向右,结合从右向左

逻辑短路问题 : 有逻辑运算参与    

     int a=5,b=6,c=7,d=8,m=2,n=2;
    (m=a<b)||(n=c>d);    //m=1  ||或 为真所以右边的不会执行  n的值不变
    printf("%d\t%d\n",m,n);   //1  2
    (m=a<b)&&(n=c>d);   //true && fause  m=1 n=0
    (n=c>d)||(m=a<b);   //n=0 m=1  ,||只要有一个为真最后的结果就为真

    (n=c>d)&&(m=a<b); //逻辑短路  右边不会被运行

     应用:
    if(strlen(name)!=0 && strcmp(name,"admin") ==0 ){}  保证了程序的健壮性
    //name ==NULL 后面就不会执行
    递归终结条件
    int test(int n,int *sum)
    {
        n && test(n-1,sum);
        return *sum += n;
    }
    int ret =0;
    test(100,&ret);   //1+2+3.....+100
如果说不能使用循环,那么第一个应该想到 递归 重视逻辑短路问题

分支


    int a=4;
    switch( a>5 ){ case 0: ...}   //a>5 判断表达式  假0  case 0会被执行
        switch( 3.14 )   //报错  不能放 float double
        //只能放整型(数)   ---char bool short long 枚举
        对于switch 控制表达式应该是整数类型,case也一样
        并且 case表达式转换后的值,不应该是相同的值 'a' ==97
        
    switch(a-4){  //2  普通表达式
        case 0:   ; break;
        case 1:   ;  break;
        
        case 2:   ;  break;  
        
        default:  //other
            printf("");
    }
   16.二维数组和一维数组的相互转化

   将一维数组中的元素分别存入二维数组中

    int a[9] ={1,2,3,4,5,6,7,8,9};   
        int b[3][3];
    (1).代码实现:
        int i, j, k=0;
        for(i=0; i<3; i++){  //控制行
            for(j=0; j<3; j++){  //控制列
                b[i][j]=a[k]; k++;
            }
        }
    (2).借助边界
        for(i=0; i<9; i++){   //控制数组a  
            b[0][i] = a[i];   //赋值
        }
    (3).运算:
        for(i=0; i<9; i++){
            b[i/3][i%3] =a[i];
        }
        

        二维数组转化为一维数组:
        int array1[A][B] ={1,2,3,4};   int array2[A*B];   int k = A*B;
        for(i=0; i<A; i++){
            for(j=0; j<B; j++){
                array2[k] = array1[i][j];  //将二维数组的值一个个存入一维数组中
                k++;
            }
        }

strcpy() 和memcpy()的区别?

        strcpy()  字符串拷贝,自动拷贝一个'\0'

        memcpy(char *dst, const char *src,  size_t  len );  内存拷贝

        区别:

                strcpy()只能使用在字符串上,会自动拷贝'\0'

                memcpy() 适用于各种类型,必须要指定长度

char  name[10];

gets(name);                 //当 name length ,超过10,内存越界,可能出错

指针

        char a;        //开辟一个字节的空间

        char  *str =&a;

        strcpy(str, "hello");           //6 个字节的空间,越界 可能导致程序崩溃

int  main()

{

char  *str = "abcd";   //str--栈区   "zbcd"--静态数据区-常量  不允许更改

str[2] = 'f';        //程序崩溃  段错误--试图去修改不属于你的内存空间

}

int  *p;---->malloc(p); ----->free(p) ----->p= NULL;

自己开辟的空间free之后一定要记得置空---清理

数组和指针相结合?/

        int  a[5] = {1,2,3,4,5};

        int   *ptr =(int *)(&a+1);   //&a[0] --取的是整个数组,指向数组尾部

        printf("%d  %d\n", *(a+1), *(ptr-1) );        //2, 5

        a[0] 第一个元素的首地址,  第一个字节 ,用首地址来表示整个数组

不定长函数参数

        (传参1,传参2,...)

        printf(const char *format, ...);    //也是一个不同的函数,可以传不同的参数,打印出来

                //  ...  可以传任意长度的参数---变长参数

        va_start(va_list arg_ptr ,prev_param);
         //以固定参数的地址作为起点,确定变长参数的内存起始地址,获取第一个变长参数的首地址
            va_arg(va_list arg_ptr,type);  //获取每个变长参数  返回的参数类型
            va_end(va_list arg_ptr);  //将va_list 类型的arg_ptr置0
    

const    不可改变的变量

        char   const  *p1;

        const  char   *p2;    //p1 and  p2可以指向不同的空间,指向的变量的内容不能改变

        char  *const  p3;    //不可以指向其它空间,可以修改里面的内容

26.函数返回值

int fun(int val)
{
    int a =val;
    a++;
    return &a;   //局部变量的地址,函数结束,这片内存就销毁了
}

int main()
{
    int a = 10;
    int *val = fun(a);   //可能会出现段错误
}
   // 多文件编程 全局变量(同名变量)不可以定义在多个.c 文件中
    //static 修饰 变成静态变量,模块化只能在本文件中使用
    
    作用域大的值和作用域小的值同名,则会使用作用域小的值

题1:变量的声明和定义有什么区别

为变量分配地址和存储空间的称为定义,不分配地址的称为声明。
一个变量可以在多个地方声明,但是只在一个地方定义。
加入extern修饰的是变量的声明,说明此变量将在文件以外或在文件后面部分定义。
题2:写出bool 、int、 float、指针变量与“零值”比较的if语句

bool型数据:
if( flag ) { A; } else { B; }

int型数据:
if( 0 != flag ) { A; } else { B; }

指针型数:
if( NULL == flag ) { A; } else { B; }

float型数据:
const float EPSINON = 0.000001;
if (( flag >= -EPSINON ) && ( x <= EPSINON )) { A; }

注意:应特别注意在int、指针型变量和“零值”比较的时候,把“零值”放在左边,这样当把“==”误写成“=”时,编译器可以报错,否则这种逻辑错误不容易发现,并且可能导致很严重的后果。
题3:sizeof和strlen的区别

sizeof和strlen有以下区别:
sizeof是一个操作符,strlen是库函数。
sizeof的参数可以是数据的类型,也可以是变量,而strlen只能以结尾为‘\0‘的字符串作参数。
编译器在编译时就计算出了sizeof的结果。而strlen函数必须在运行时才能计算出来。
sizeof计算的是数据类型占内存的大小,而strlen计算的是字符串实际的长度。
数组做sizeof的参数不退化,传递给strlen就退化为指针了。
题4:C中的malloc和C++中的new有什么区别

malloc和new有以下不同:

(1)new、delete 是操作符,可以重载,只能在C++中使用。

(2)malloc、free是函数,可以覆盖,C、C++中都可以使用。

(3)new 可以调用对象的构造函数,对应的delete调用相应的析构函数。

(4)malloc仅仅分配内存,free仅仅回收内存,并不执行构造和析构函数

(5)new、delete返回的是某种数据类型指针,malloc、free返回的是void指针。

注意:malloc申请的内存空间要用free释放,而new申请的内存空间要用delete释放,不要混用。因为两者实现的机理不同。
题5:说明关键字volatile有什么含意

volatile的作用:

作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值。

一个定义为volatile的变量就是说这个变量可能会被意想不到的改变,这样,编译器就不会去随便假设这个变量的值了。精确的说,优化器在用到这个变量的值的时候,必须每次都小心的重新读取这个变量的值,而不是使用保存在寄存器里面的备份。

下面是volatile变量的几个例子:

1). 并行设备的硬件寄存器(如:状态寄存器)

2). 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)

3). 多线程应用中被几个任务共享的变量
题6:写一个“标准”宏MIN

#define min(a,b) ( (a)<=(b)?(a):(b) )
注意:在调用时一定要注意这个宏定义的副作用,如下调用:
min(++*p,x) 展开为 ((++*p)<=(x)?(++*p):(x)。
p指针就自加了两次,违背了MIN的本意。
题7:设置地址为0x67a9的整型变量的值为0xaa66

*(int *)0x67a9 = 0xaa66;
题8:链表和数组有什么区别

数组和链表有以下几点不同:
(1)存储形式:数组是一块连续的空间,声明时就要确定长度。链表是一块可不连续的动态空间,长度可变,每个结点要保存相邻结点指针。
(2)数据查找:数组的线性查找速度快,查找操作直接使用偏移地址。链表需要按顺序检索结点,效率低。
(3)数据插入或删除:链表可以快速插入和删除结点,而数组则可能需要大量数据移动。
(4)越界问题:链表不存在越界问题,数组有越界问题。
说明:在选择数组或链表数据结构时,一定要根据实际需要进行选择。数组便于查询,链表便于插入删除。数组节省空间但是长度固定,链表虽然变长但是占了更多的存储空间。
题9:用变量a定义

一个整型数 int a;
一个指向整型数的指针 int *a;
一个指向指针的指针,它指向的指针是指向一个整型数 int **a;
一个有10个整型数的数组 int a[10];
一个有10指针的数组,该指针是指向一个整型数 int *a[10];
一个指向有10个整型数数组的指针 int (*a)[10];
一个指向函数的指针,该函数有一个整型数参数并返回一个整型数 int (*a)(int);
一个有10个指针的数组,该指针指向一个函数,该函数有一个整型数参数并返回一个整型 int (*a[10])(int);
题10:引用和指针的区别

(1). 指针是一个实体,而引用仅是个别名;
(2). 引用使用时无需解引用(*),指针需要解引用;
(3). 引用只能在定义时被初始化一次,之后不可变;指针可变;
(4). 引用没有 const,指针有 const,const 的指针不可变;
(5). 引用不能为空,指针可以为空;
(6). “sizeof 引用”得到的是所指向的变量(对象)的大小,
而“sizeof指针”得到的是指针本身(所指向的变量或对象的地址)的大小;
(7). 指针和引用的自增(++)运算意义不一样;
题11:static的用法(定义和用途)

1)用static修饰局部变量:使其变为静态存储方式(静态数据区),那么这个局部变量在函数执行完成之后不会被释放,而是继续保留在内存中。

2)用static修饰全局变量:使其只在本文件内部有效,而其他文件不可连接或引用该变量。

3)用static修饰函数:对函数的连接方式产生影响,使得函数只在本文件内部有效,对其他文件是不可见的(这一点在大工程中很重要很重要,避免很多麻烦,很常见)。这样的函数又叫作静态函数。使用静态函数的好处是,不用担心与其他文件的同名函数产生干扰,另外也是对函数本身的一种保护机制。
题12:const的用法(定义和用途)

const主要用来修饰变量、函数形参和类成员函数:

1)用const修饰常量:定义时就初始化,以后不能更改。

2)用const修饰形参:func(const int a){};该形参在函数里不能改变

3)用const修饰类成员函数:该函数对成员变量只能进行只读操作,就是const类成员函数是不能修改成员变量的数值的。

被const修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。

const int a; 
int const a; 
const int *a; //等于 int const *a;
int * const a; 
int const * const a; //等于 const int* const a;

前两个的作用是一样,a是一个常整型数。
第三个意味着a是一个指向常整型数的指针(也就是,整型数是不可修改的,但指针可以)。
第四个意思a是一个指向整型数的常指针(也就是说,指针指向的整型数是可以修改的,但指针是不可修改的)。
最后一个意味着a是一个指向常整型数的常指针(也就是说,指针指向的整型数是不可修改的,同时指针也是不可修改的)。
方便记忆:

以*为分界点,
当const在*的左边时,实际物体值不变
当const在*的右边时,指针不变,
即, 左物右指
这样来记比较方便!!

题13:内存四区

由C/C++编译的程序占用的内存分为以下几个部分
1.代码区:存放函数体内的二进制代码,有操作系统管理。
2.全局区:存放全局变量,静态变量以及常量。
这部分可以细分为data区和bss区
2.1 .data区
data区里主要存放的是已经初始化的全局变量、静态变量和常量
2.2 .bss区
bss区主要存放的是未初始化的全局变量、静态变量,这些未初始化的数据在程序执行前会自动被系统初始化为0或者NULL
2.3 常量区
常量区是全局区中划分的一个小区域,里面存放的是常量,如const修饰的全局变量、字符串常量等
3.栈区:由编译器自动分配和释放,存放函数参数,局部变量等
4.堆区:由程序员分配释放,若程序员未释放,则程序结束之后,编译器自动释放。
注意:
const修饰的全局变量也储存在常量区;
const修饰的局部变量依然在栈上。
有的分为5个区,即代码区,全局区,常量区,栈区,堆区

#include<stdio.h>
#include<malloc.h>

int a = 0; //全局初始化区 .data段

char *p1; //全局未初始化区 .bss段
void main()
{
    int b;// 栈
    char s[] = "abc"; //s在栈上,大小4字节。 "abc\0"为s的初始值,可以不用纠结其存在哪里
    char *p2; //栈
    char *p3 = "123456"; //"123456\0"在常量区,p3在栈上。
    static int c =0; //全局(静态)初始化区 .data段
    p1 = (char *)malloc(10);
    p2 = (char *)malloc(20);
    //分配得来的10和20字节的区域就在堆区。
    strcpy(p1, "123456"); //"123456\0"放在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方。
}

题14:大小端问题

大端:一个多字节整数,数字的高位部分存放在内存的低地址单元。低地址存高字节
小端:一个多字节整数,数字的低位部分存放在内存的低地址单元。低地址存低字节
大小端格式问题。
方法一:

 void checkCpuMode(void)
 {
  int i = 0x12345678;
  char *cp = (char *)&i;
  if(*cp == 0x78)
   printf("little endian");
  else
   printf("big endian\n");
 }

 void checkCpuMode(void)
 {
  int a = 0x12345678;
  if((char)a == 0x12)
   printf("big endian\n");
  else
   printf("little endian\n");
 }

方法三:

void checkCpuMode(void)
{
  union
  {
   short s;
   char c[sizeof(short)];
  }un;
  un.s=0x0102;
  if(un.[0]==1&&un.c[1]==2)
   printf("big endian\n");
  else
   printf("little endian\n");
 }

静态局部变量在什么时候分配内存

1、
① static局部变量在编译阶段就已经分配内存空间了,也就是函数没有调用前它就已经存在了。
② 普通局部变量只有运行到定义该变量的时候才会分配内存空间。
2、
① 当执行完定义该static局部变量的函数体后,该static变量的内存空间不会被释放,只有程序结束时static变量才会自动释放。
② 当执行完定义该普通局部变量的函数体后,该普通局部变量的内存空间就会被释放。
3、
① 如果static局部变量不初始化,那么它默认为0。
② 如果普通局部变量不初始化,那么它的值为随机数。
4、
① static局部变量的初始化语句只会执行一次,下次再运行该初始化语句就不会被初始化,但是它可以被多次赋值。
② 每次运行该普通局部变量的定义时该变量都会被初始化。

#define宏定义与typedef的区别

#define宏定义是字符替换,typedef是定义类型,是声明一种新的类型,等同自带的基本类型。

#define是宏,处理的时候位于编译前阶段,宏处理器基本上对你的C/C++程序不会有任何的感知。它只处理宏的语法。而编译阶段的“程序”得到的是宏处理完的结果。

typedef是编译阶段的一部分。它的意义是单一的。

宏定义只是简单的字符串代换,是在预处理完成的,而typedef是在编译时处理的,它不是作简单的代换,而是对类型说明符进行重新命令。被命名的标识符具有类型定义说明的功能。

#include<stdio.h>

#define PIN1 char*

typedef char* PIN2;

void main()
{
    //使用#define相当于:char* x, y;就是说x是char指针类型,而y是char类型,如果要连续定义两个指针需要写成char *x, *y;
    PIN1 x, y;
    //使用typedef相当于:char* x, char* y;就是说x是char指针类型,而y也是char指针类型
    PIN2 a, b;
    printf("By #define :%d  %d\n\n",sizeof(x), sizeof(y));
    printf("By typedef :%d  %d\n\n", sizeof(a), sizeof(b));
    //system("pause");
    return 0;
}

值传递改变值这类问题

下面的代码有什么问题?

void GetMem(char *p)
{
    p = (char*)malloc(100);   
}
 
void main()
{
    char *str = NULL;
    GetMem(str);
    strcpy(str, "hello word!");
 
    printf(str);
}

分析:

程序崩溃。在上面已经分析过了,传递给GetMem函数形参的只是一个副本,修改形参p的地址对实参str丝毫没有影响。所以str还是那个str,仍为NULL,这时将字符串常量拷贝到一个空地址,必然引发程序崩溃。下面的方法可以解决这个问题:

void GetMem(char **p)
{
    *p = (char*)malloc(100);   
}
 
void main()
{
    char *str = NULL;
    GetMem(&str);
    strcpy(str, "hello word!");
    printf(str);
    free(str);   //不free会引起内存泄漏
}

void GetMem(char **p)
{
    *p = (char*)malloc(100);   
}
 
void main()
{
    char *str = NULL;
    GetMem(&str);
    strcpy(str, "hello word!");
    printf(str);
    free(str);   //不free会引起内存泄漏
}

计算sizeof的值

char str1[] = {'a', 'b', 'c', 'd', 'e'};
char str2[] = "abcde";
char *ptr = "abcde";
char book[][80]={"计算机应用基础","C语言","C++程序设计","数据结构"};

sizeof(str1)=?

sizeof(str2)=?

sizeof(ptr)=?

sizeof(book)=?

sizeof(book[0])=?

分析:

sizeof(str1)=5,就是5*sizeof(char)=5;

sizeof(str2)=6,字符串都是以’0’结尾,所以所占字节数为6;

sizeof(ptr)=4,ptr是一个指针,在32位平台上大小为4字节;

sizeof(book)=320,book是一个二维数组,4*80*1

sizeof(book[0])=80,book[0]是第一维数组,因为此80*1

根据sizeof求数组元素的个数也很简单,拿第一个来说,就是sizeof(str1)/sizeof(char)。

char  arryA[] = {'a','b','c',0,'d','e'};
char  arryB[] = {'a','b','c','d','e'};
char  arryC[6] = {'a','b','c','d','e'};
char *str = "abcde";

分析:

strlen(arryA) = 3,strlen遇到’\0’就会返回,无论后面有多少个字符;

strlen(arryB)长度无法确定,没有人为写入‘\0’,strlen会继续计算直到找到结束符,结果未知;

strlen(arryC)=5,指定了数组大小,编译器会自动在空余地方添加’\0’,这其实跟char arryC[6] = {‘a’,‘b’,‘c’,‘d’,‘e’,‘\0’};等价。

strlen(str) = 5,不包括结尾的’\0’。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值