c语言编译的四个过程 在C语言编译的四个过程中,通常指的是预处理、编译、汇编和链接。下面是这四个过程的简要描述和对应的命令行操作: 预处理 处理源代码中的预处理指令,例如#include、#define、#ifdef等。使用命令行工具gcc -E进行预处理。 gcc -E source_file.c -o preprocessed_file.i 编译 将预处理后的文件转换成汇编语言。使用命令行工具gcc -S进行编译。 gcc -S preprocessed_file.i -o compiled_file.s 汇编 将汇编语言代码转换成目标机器指令。使用命令行工具gcc -c进行汇编。 gcc -c compiled_file.s -o assembled_file.o 链接 将各个模块的目标文件和所需的库文件进行合并,生成可执行文件。使用gcc本身或者其他链接工具如ld进行链接。 gcc assembled_file.o -o executable_file 以上命令假设你的源文件名为source_file.c。在每一步中,输出文件的名称可以自定义,但通常遵循了预处理、编译、汇编和链接的顺序命名。 int 八进制 书写时第一位是0,每一位数字必须是0-7 十六进制 书写是以0x开头或者0X开头 可以是0~9,a~f或者A~F 长整型 long int a = 10L; 有符号数 最高位是符号位 0表示正数 1表示负数 计算一个变量或表达式占多少内存 sizeof(类型名) 或 sizeof 表达式 或 sizeof(表达式) 会计算'\0'所占长度 char 通常每一个字符数据 (常量或者变量) 在内存中占一个字节,所存储的数值是该字符的ASCII码,也就是一个0~255之间的整数 赋值表达式 V=E 计算表达式E的值,将计算结果赋值给V,并取E的值为整个赋值表达式V=E的值。 复合运算符 += 引入复合运算符的目的一是简化书写,二是提高运算效率 下面三条语句的功能都是使变量j的值增加1,但运算效率不同 1、 j++; 2、 j+=1; 3、 j = j+1; 其中,1,2只对变量j进行一次地址解码,而3对j进行两次地址解码。2和3都进行一次表达式求值计算,而1不需要进行表达式求值计算。 printf(); "%m.ns"用于字符串输出,表示共占m列,但只取字符串左端的n个字符,这n个字符输出在m列的右侧,左边补空格。 用o或x或u输出整数时,则认为数值不带符号位,而将符号位看作数值的一部分,因为,若输出负的整数时,将导致错误。 如果想输出%,需要在格式控制串中连续写两个%,即连续两个%。 输入 scanf() getchar() 不带参数的输入函数 返回值char gets(char a[10]) 输入一个字符串 输出 printf() putchar(char c) 将一个字符输出到标准设备 puts(char c[10]) 输出一个以'\0'为结尾的字符串 ,输出是将'\0'转换为'\n' strlen(string) 不包含结束符"\0" strcat(str1,str2) 将str2中所存放的字符串连接到str1所存放的字符串后面,构成一个更大的字符串,并把新串存放在字符数组str1中,函数 返回str1的起始地址,连接时,str1尾部的'\0'将被去掉,但保留结束串的结束符'\0' str1必须足够长 strcpy(str1,str2) 将str2中的有效字符 不包含'\0'复制到str1中,str1必须足够长,不能直接使用str1 = str2 因为数组名时地址(常量),是不能被赋值的。 k = strcmp(str1,str2) 字符串比较函数 k是整型变量,将str1和str2的对应字符从左到右,按ASCII码值大小逐对比较,直到出现不相等的字符, 或遇到'\0'为止,将比较结果返回,k<0 str10 str1>str2 以第一个不同字符作为整个字符串的比较结果。'\0'小于其他字符。 strstr(str1,str2) 子串匹配函数 在str1中寻找串str2 第一次出现的地址,没有返回空指针。 char *strdup(const char *s) strdup()函数主要是拷贝字符串s的一个副本,由函数返回值返回,这个副本有自己的内存空间,和s没有关联。 strdup函数复制一个字符串,使用完后,要使用freee函数释放在函数中动态申请的内存,strdup函数的参数不能为NULL,一旦为NULL,就会报段错误, 因为该函数包括了strlen函数,而该函数参数不能是NULL。 函数参数的传递 1、传值 传值方式的特点是:形参是一个简单类型,即整型、实型、字符型等的变量形式,而对应的实参是一个(类型相同,或可赋值的)表达式。在函数执行期间,只有形参 起作用,而实参不起作用。形参的值若发生变化,影响不到实参。 2、传地址(实质上也是传值) 对形参而言,它接受的还是值,只不过该值是变量的地址。传地址根据形参变量的类型,又可以再具体分成传指针和传数组两种。 传数组是指当形实参数都是数组名时,进行参数传递,传的就是数组的地址即首元素地址,准确的说,在形实结合期间,形参数组名获取的是实参数组的地址。 传指针 void swap(int *a ,int *b) 就是传地址 swap(&a,&b); 3、传引用 c++ 使用引用声明符& 可以定义一个引用 一般格式为 引用类型名 & 引用名 引用的作用是为一个变量起一个别名,引用既可以用在函数参数及返回值,也可以用在普通变量 int a; int &r = a; //&前后的而空白无意义 变量a的引用是r,a是r的引用对象 程序中凡是用到a的地方都可以用r代替,a即是r,r即是a a=123 和 r=123一样 一旦定义了引用,其引用对象就固定不变了,不能在改为对另一个变量int b的引用。换言之,引用本身不是变量,它没有对应的存储单元,它的值就是 被引用对象的名字,即地址,也可以说引用不能被修改,通过引用而修改的只是它引用对象的值。 int &r2 = r 相当于 r2 = a 变量的存储属性 变量的存储属性主要用来指定变量的生存期,即从为它分配存储单元到收回它所占的存储单元的时间段。分别是 auto 自动的 自动变量 auto可以省略 所以未加存储属性说明的变量均为自动变量 static 静态的 静态变量 register 寄存器的 寄存器变量 extern 外部的 外部变量 在定义变量是 一般格式 存储属性 类型名 变量名 内存被分为系统区和用户区 用户区又分为代码区和数据区,数据区分为静态存储区和动态存储区,分配到静态区的变量具有最长生存期,直到程序运行结束,分配在动态区的变量用完 即释放。 静态区变量有 全局变量 static 定义的局部变量 动态区变量有 函数的行参 函数内部定义的局部变量 用于函数嵌套调用的栈区 寄存器变量 C语言允许用户将几个局部变量(注意:只能是局部变量或形参,而不是全局变量,也不能是静态变量)定义为寄存器变量,系统不为它们分配内存单元, 而是直接将其存放在寄存器中。寄存器变量的生存周期等同于自动变量。如果在一个函数里定义的寄存器变量过多,那些没有分配到寄存器的变量,系统 将会按自动变量处理。 另外,带有优化功能的编译器,能够自动识别使用频繁的变量,并会自动将其改为寄存器变量。 不允许其他源程序文件使用的全局量 定义方式 前面添加 static static 类型名 变量名 使用其他源程序文件中定义的全局量 extern 类型名 全局变量名 二维数组 二位数组a[M][N]的数组名代表二位数组的首地址,这个地址既等于第一个行元素a[0]的地址,也等于第一个列元素a[0][0]的地址。但a却是一个行地址 a = a[0] 的地址 a+1 = a[1] 的地址 a[0]+1 = a[0][1] 的地址 int a[2][3] = {1,2,3,4,5,6}; printf("&a = %p\n",a); printf("&a+1 = %p\n",a+1); printf("&a[0]+1 = %p\n",a[0]+1); printf("*a[0]+1 = %d\n",*(a[0]+1)); printf("&a[1] = %p\n",a[1]); printf("&a[2] = %p\n",a[2]); printf("&a[0][0] = %p\n",&a[0][0]); printf("&a[0][1] = %p\n",&a[0][1]); printf("&a[1][0] = %p\n",&a[1][0]); printf("&a[2][0] = %p\n",&a[2][0]); &a = 000000000061FDD0 &a+1 = 000000000061FDDC &a[0]+1 = 000000000061FDD4 *a[0]+1 = 2 &a[1] = 000000000061FDDC &a[2] = 000000000061FDE8 &a[0][0] = 000000000061FDD0 &a[0][1] = 000000000061FDD4 &a[1][0] = 000000000061FDDC &a[2][0] = 000000000061FDE8 在行地址签名加运算符* 就表示将行地址转换成列地址。虽然地址值没变但地址的行列性质改变了。 *a等价于a[0] (*a)+1等价于a[0]+1 *(a+1)等价于a[1] *(a+1)+1 等价于a[1]+1 由于二维数组int a[M][N]的列元素是int类型,所以任何指向int的指针都可以作为数组a的列元素指针。 例如: int a[M][N] , *p; 由于二维数组的行元素是一个一维数组,所以定义指向二维数组元素的指针时,必须使其指向一维数组,而不是指向一维数组的元素 例如:int (*q)[N]; 这样指针变量q便是指向一维数组的指针,这里的N是一维数组的长度或二维数组的列数。 但要注意,定义二维数组的行指针时,*q两边的括号不能少,如果定义成 int *q[N]; 那么,由于运算符[]的优先级高于* 的优先级,q便是一个数组名,q的每个元素是一个int类型的指针,即q是一个指针类型的数组。 可以将一个字符串常量赋值给字符型指针,使该指针指向这个串,但却不能将一个字符串常量赋值给字符数组名。 例如 char a[30] *p; 则语句 p = "hello world"; 是正确的 a = "hello world"; 是错误的 原因在于编译器遇到字符串常量时,首先为该串分配所需要大小的存储空间,然后把该空间的首地址赋值给指针变量,由于p时变量所以给p赋值是允许的。 而数组名a是符号常量,故不能修改。 但是,在定义字符数组变量的同时赋值的语句 char a[] = "hello world"; 是正确的 调用malloc()函数动态产生的变量就是动态变量 #include malloc(size)的功能是分配size个字节的存储区,并将所分配存储区的首地址作为函数的返回值。由于malloc函数返回的是void类型的指针, 因为使用时,需要把指针强制转换为指针变量所对应的类型,否则运行时可能出现错误。 动态分配函数返回值为所分配的内存单元首地址,若分配失败则返回空指针NULL(即0); int *p; p = (int*)malloc(sizeof(int)); *p = 3; calloc(n,size)函数的功能是分配n个单元的连续内存空间,每个单元占size字节。该函数可产生长度可变的动态数组。 int *p ,*q; unsigned n,i; scanf("%f",&n); q=p=(int*)calloc(n,sizeof(int)); for(i=0;i