**************************************************************************************************************************************************************************************************************** 操作符解释: 1,算术操作符: ‘+’、‘-’、‘*’、‘/’、‘%’ %取模操作符只能用于整数, /除法操作符,两个操作数均是整数时为整数除法,有一个是浮点数则执行浮点数除法。 如:int a = 6/5;//->a==1 double b = 6/5;//->b==1.000000 double c = 6.0/5//->c==1.200000,即实际运算与定义的储存类型无关 2,移位操作符 左移‘<<’、右移‘>>’ 左移操作符: 如: int a=2; int b = a << 1;//a向左移动一位,结果存入变量b中,结果b为4 正整数2在内存中存放的补码为: 00000000000000000000000000000010 向左移动一位,左边的首位0去掉,右边的缺一位补0:00000000000000000000000000000100 ->4(十进制) 右移操作符: 右移时分为算数右移和逻辑右移。 算数右移:右边丢弃,左边补符号位。 逻辑右移:右边丢弃,左边补0。 如:int a = -10; int b = a >> 1;//a向右移动一位,结果存入变量b中 正整数10在内存中的补码为11111111111111111111111111110110//若为负数则原码与补码不同。 算术右移: 11111111111111111111111111111011 ->-5 逻辑右移: 01111111111111111111111111111011 ->2,147,483,643 对移位操作符移动负数位,这时C语言未定义的,不要使用。 3,位操作符 按位与‘&’、按位或‘|’、按位异或‘^’ 位解释为二进制的一位。 &按位与,全为1时结果才为1,其他情况结果均为0 如:int a = 3;// 00000000000000000000000000000011 int b = 5;// 00000000000000000000000000000101 int c = a & b;// 00000000000000000000000000000001->1(十进制) eg:找出一个整数的二进制的1的个数。 #include <stdio.h> int main(){ int a=1; int cnt=0; for(int i=0; i<31; i++){ if((a&1) == 1){//整数的二进制数的1的个数 cnt++; } a=a >> 1; } printf("%d\n",cnt); return 0; } |按位或,全为0时结果才为0,其他情况结果均为1 如:int a = 3;// 00000000000000000000000000000011 int b = 5;// 00000000000000000000000000000101 int c = a | b;// 00000000000000000000000000000111->7(十进制) ^按位异或,不同为1,相同为0。一个数a与另一个数b异或两次结果仍是a本身,数a与0异或结果仍为数a。 如:int a = 3;// 00000000000000000000000000000011 int b = 5;// 00000000000000000000000000000101 int c = a ^ b// 00000000000000000000000000000110->6(十进制) eg:交换两个数a=3,b=5,要求不使用第三个变量。 #include <stdio.h>//此法适用于a,b相加的结果不超过类型范围的情况,有缺点 int main(){ int a=3; int b=5; a = a + b;//变量a储存a与b的和 b = a -b;//变量b储存原来a的值 a = a - b;//变量a储存原来b的值 return 0; } #include <stdio.h>//此法适用于a,b相加的结果不超过类型范围的情况,有缺点 int main(){ int a=3; int b=5; a = a ^ b; b = a ^ b;//3 ^ 5 ^ 5的结果3存入到b中 a = a ^ b;//3 ^ 5 ^ 3的结果5存入到a中 return 0; }; 4,赋值操作符 = += -= *= /= %= <<= >>= ‘=’是赋值,‘==’是等号。 int a = a + 4;//等价于a+=4; 连续赋值: 如:int b = a = a+1;//等价于a = a + 1; b = a; 连续赋值不好进行调试,阅读不便。 5,单目操作符 !(逻辑反) -(负值) +(正值) ~(按位取反) sizeof(操作数的类型长度,按字节计算) -- ++ &(取地址) *(间接访问操作符或解引用操作符) (类型名)强制类型转化 ‘i’一般用于条件判断,0的反是1,非0的反是0; 如: #include <stdio.h> int main(){ int flag =0; if(flag){ printf("Yes\n");//条件为假,语句不执行 } if(!flag){ printf("YesYes\n");//条件为真,语句执行 } return 0; }; ‘~’对二进制位的操作,类比按位与、按位或、按位异或。最高位也参与取反。 如:int a = 13;//00000000000000000000000000001101 int b = ~a; //11111111111111111111111111110010 -> b==-14; ‘sizeof’是单目操作符,可求内存大小。 如: int a =13; int b = sizeof(a);//结果为4,是一个int类型变量所占字节个数,也可写为int b = sizeof a; 不需要加括号 int arr[10]={0}; int c = sizeof(arr);//结果为40,是十个int类型变量所占字节个数 int g = sizeof(int [10]);//结果为40,数组的类型实际上是去掉数组名(arr)之后留下的(int [10])。 如: #include <stdio.h> int main(){ short s = 5; int a = 10; printf("%d\n",sizeof(s = a + 2));//输出为2,short占2字节,等价于printf("%d\n", sizeof(s));。 printf("%d\n",s);//输出为5,上面表达式没有计算 return 0; }; ‘++’ int a = 13; ++a;//前置++,先++在使用 a++;//后置++,先使用,在++ ‘&’取地址操作符,单目操作符,与按位与操作符区分 ‘*’解引用操作符或间接引用操作符 如:#include <stdio.h> int main(){ int a =13; int *p = NULL;//定义int类型的指针变量,此时 * 是定义 p = &a;//整型指针变量p中存放整型变量a的地址 printf("%d\n",*p);//此句输出为13,*p与a等价。此时 * 是解引用 int a =1; char b=1; double c=1; float d=1; int *pa =&a; char *pb =&b; double *pc=&c; float *pd=&d; printf("%d\n",sizeof(pa)); printf("%d\n",sizeof(pb)); printf("%d\n",sizeof(pc)); printf("%d\n",sizeof(pd));//输出相同,为4或8(与具体系统有关),求的是指针变量的大小,不论是那种类型的指针变量,储存的地址形式都相同,内存的地址(指针常量)形式是确定的 } ‘(类型名)’, 如:#include <stdio.h> int main(){ int a = 3.14;//浮点型转换为int型会损失精度,产生警告。 int b =(int)3.14;//强制类型转换,没有警告。 return 0; } 6,关系操作符 =(赋值) ==(相等) <、>、<=、>=、!=(不等于) 字符串比较不能用==,要用字符串函数 7,逻辑操作符 &&(逻辑与) ||(逻辑或) 首先和按位与(&)、按位或(|)区分开。 逻辑与:全真为1,其它为0。 逻辑或:全0为0,其它为1。 短路现象:对于逻辑与: 如: #include <stdio.h> int main(){ int a=0,b=3,c=2,d=4; if(a && b && c && d)//先看a && b,a==0,则最终表达式结果一定为0,后面表达式不再计算 printf("111\n"); else{ printf("000\n");//最后输出为000 } } 对于逻辑或: 如: #include <stdio.h> int main(){ int a=5,b=3,c=2,d=4; if(a || b || c || d)//先看a || b,a == 5,则最终表达式结果一定为1,即为真,后面表达式不再计算,输出为111。 printf("111\n"); else{ printf("000\n"); } } 8,条件操作符 ? : (三目操作符) 如:#include <stdio.h> int main(){ int a =2; int b = 4; printf("%d\n", a>b?a:b);//如果a>b表达式成立,则表达式结果为a,否则为b,输出为4。 return 0; } 9,逗号表达式 逗号表达式,从左向右依次计算,整个表达式的值是最后一个子表达式的值。 #include <stdio.h> int main(){ int a =2; int b = 4; printf("%d\n", (a=a+3,b=b-2));//表达式a = a + 3计算,但整个表达式(a=a+3,b=b-2)的值是表达式(b=b-2)的值,结果为2 b = 4; printf("%d\n", b=b-2);//结果为2 } 10,下标引用、函数调用和结构成员访问 [ ]下标引用操作符(运算符) 操作数:一个数组名+索引值,类比二元操作符,1 + 2。 如: #include <stdio.h> int main(){ int a[10];//创建数组 a[0] = 10;//使用下标引用操作符为数组第一个单元赋值 } ( )函数调用操作符,接受一个或多个操作数,第一个操作数是函数名,剩余的操作数是函数的参数。 如: #include <stdio.h> int max(int a, int b){//函数定义 return a > b ? a : b; } int main(){ int a = 3; int b = 4; max(a, b);//函数调用,输出二者中较大的那个数。 printf("%d\n",t); return 0; } 结构成员访问操作符:对于非指针变量使用圆点操作符,指针变量还可以使用箭头操作符 如: #include <stdio.h> struct student{ char name[20]; int age; double mathscore; }; int main(){ struct student a = { "weihe", 20, 90}; printf("%s\n",a.name);//使用圆点操作符访问结构体成员name数组 struct student *p = NULL; p = &a; printf("%d\n", (*p).age);//使用指针解引用访问结构体成员age printf("%d\n", p->age);//使用箭头操作符访问结构体成员age return 0; } 11,表达式求值 求值顺序由操作符的优先级和结合性决定。有些表达式的操作数再求值的时候可能要转换为其他类型。 12,隐式类型转换 C的整型算术运算是以缺省(sheng)整形类型的精度来进行的。 为了达到这个缺省(sheng)整形类型的精度,表达式中的字符和短整型操作数(2字节)在使用之前被转换为普通整型(int或unsigned int),这种转换称为整形提升。 整形提升的意义: 表达式的整型运算在CPU相应的运算器件内执行,CPU内整型运算器的操作数的字节长度一般是int的字节长度,同时也是CPU的通用寄存器的长度。 即使是两个字符型变量相加,CPU执行时实际上也要先转换为CPU内整型操作数的标准长度。 通用CPU难以直接实现两个8bit字节的直接相加运算(虽然机器指令中可能有这种字节相加指令)。 整形提升过程: 按照变量数据类型的符号位提升。 正数: char a = 1;//00000001(补码) ->(整形提升) 00000000000000000000000000000001 负数: char b = -1//11111111 (补码)-> (整形提升)11111111111111111111111111111111 如: #include <stdio.h> int main(){ char a = 3; char b = 127; char c = a + b;// a: 00000011 -> 00000000000000000000000000000011 // b: 01111111 -> 00000000000000000000000001111111 c: 00000000000000000000000010000010 ->(截断) 10000010 printf("%d\n", c);/ c: 10000010 -> 11111111111111111111111110000010(反码)->(求原码)10000000000000000000000001111110 即为-126 return 0;//以int型输出是也会发生整型提升,对于unsigned char,整型提升时高位均补0 } #include <stdio.h> int main(){ char c =0;//sizeof返回类型为size _t,是unsigned int,打印时要用格式符%u printf("%u\n",sizeof(c));结果为1 printf("%u\n",sizeof(-c));结果为4,sizeof运算符括号里的表达式(-c)虽然不参与运算,但有类型属性,为整形提升后的int printf("%u\n",sizeof(+c));结果为4 printf("%u\n",sizeof(!c));结果为4 return 0; } **************************************************************************************************************************************************************************************************************** 字符串 相关函数 头文件#include <string.h> 求长度 char s[10] = "hello world"; strlen(s),返回字符的个数 sizeof(s),返回字符串大小,包括'\0' 复制函数 char s1[10];//字符数组 char s2[10] = "hello"; strcpy(s1,s2),把字符串s2整体复制到s1中,但s1中要有足够的内存 strncpy(s1,s2,n),对s2最多n个字符成立 比较函数 strcmp(s1,s2),相等返回0,s1>s2返回正整数,s1<s2返回负整数 strncmp(s1,s2,n),字符串s1中最多n个字符与字符串s2比较,同上 连接函数 char s1[10];//字符数组 char s2[10] = "hello"; strcat(s1,s2),把s2中字符添加到s1中有效字符之后,s1要有足够的内存 strncat(s1,s2,n),对s2最多n个字符成立 逆置函数(倒序) char s[10] = "hello"; strrev(s), 输出函数 sprintf(s,格式字符串,输出项列表),输出到字符串s中,不在屏幕上显示 输入函数 sscanf(s,格式字符串,输出项列表),从字符数组s中读取,而不是从键盘读取,
字符串数组与函数
**************************************************************************************************************************************************************************************************************** 数组 &arr//指数组的地址,(&arr+1)加了整个数组的长度 arr//指数组首元素的地址,(arr+1)加了一个数组元素的长度 arr[0]//指数组首元素的地址
二维数组的函数调用
void pow(int a[][10])//一个调用二维数组的函数 int arr[10][10]; pow(arr);//只写数组名
**************************************************************************************************************************************************************************************************************** 刷新函数 memset(数组名,0,sieof(数组名));
****************************************************************************************************************************************************************************************************************
指针(间接引用)(地址) 指针常量:计算机内存中的事先实现好的编号即地址,不可改变。 指针变量:指针类型,储存地址的变量,可以改变,指针变量自身也占有一定内存。不与指针常量混淆时简称为指针。
指针的大小: 指针在32位系统中占4字节,在64位系统中占8字节,与地址占内存的大小有关。 而在同一系统中地址由多个相同的bit构成 变量(int,float,double,char等)占用内存中的字节,首个字节的地址是该变量的地址
初始化 声明指针时必须对其进行初始化(为0,NULL,已定义变量地址)。 如: void *p = NULL;//可以指向任何类型的指针,未知指向变量的数据类型,不能进行间接寻址运算 int *p = NULL;//不指向任何对象 int *p = 0; int a = 1; char *p = &a;//指向变量a的地址
间接寻址运算(运算符*,&) *:解引用操作符,访问指针p所指的变量a。 &:取(返回)变量a的地址。 int a = 1; int *p = &a;//此时*为指针声明符 有*p与a等价,p与&a等价
指针的算术运算 加法运算:(得到另一个地址) int a[4]={4,5,3,1}; int *p = a;//或p = &a[0]; 有(p + 1) == &a[1];.......(p + i) == &a[i]; 减法运算: (得到地址之间的距离,用元素的个数表示) int a[4]={4,5,3,1}; int *p = &a[0]; int *q = &a[3]; 则p - q == 3.//结果可以为负整数,即地址相减后除以类型所占的字节 地址之间的距离==sizeof(int)*3. 指针比较:(指向相同数组时才有意义) int a[4] = {5, 4, 3, 2, 1}; int *p = &a[0]; int *q = &a[4]; for(; p<=q; p++){ printf("%d\n", *p);//循环输出数组各元素 } 指针与数组: int a[4]; 由数组名a可知数组a的内存大小并得到数组a的首元素的第一个字节的地址(即数组名a是指向a[0]的一个指针常量,,指针常量,不可更改)。 int *p = a; int i; 一个指针p与数组a关联之后数组a中的元素表示方法可以为:(以指针p指向数组首元素为例) 数组下标表示: a[i]; 数组偏移量表示: *(a + i);//偏移量,即i 指针下标表示: p[i]; 指针偏移量表示: *(p+i); 数组作为函数的参数: 形参数组(定义的函数中,非main函数)本质上是一个相应类型的指针变量,不是指针常量. 例如 int a[4];//实参 函数头void sum(int a[], int n)//形参a[]获得了数组的首元素的指针(地址),可以访问和修改数组a[4]中的所有元素 与void sum(int *a, int n)等价 指针与const限定符: (减小函数的在主函数的权限等) const修饰后,为常量,不可修改。 定义函数时 声明为从const的指针要在声明时就初始化,若为定义的函数参数则由相应主函数的实参初始化。 可变指针指向可变(可修改的)数据: void sum(char *p,int n)//权限最大,无const约束指针或类型数据(char). 可变指针指向常量(不可修改的)数据: void sum(const char * p, int n)//应从右往左理解const char * p,p是一个指针,指向字符常量,可进行指针运算。 常量指针指向可变数据: void sum (char const * p, int n)//从右往左char const * p,p是一个指针常量,指向字符型数据,可改变指针指向数据的值。 常量指针指向常量数据: void sum(const char const * p,int n)//权限最小,指针(地址)的值不能改变,其指向的数据也不能改变
指针数组:是一个数组,数组元素时一个个指针变量 char* arr[4];//一级字符指针数组 char** arr[4];//二级字符指针数组 如: #include <stdio.h> int main() { int a = 10; int b = 20; int c = 30; int *arr[3] = { &a,&b,&c };//注意[ ]优先级比*高 int i = 0; for(i=0; i<3; i++){ printf("%d\n", *arr[i]); } return 0; }
#include <stdio.h> int main() { int a[5] = { 1,2,3,4,5 }; int b[] = { 1,3,5,7,89 }; int c[] = { 2,3,4,5,6 }; int* arr[3] = { a,b,c };//先是一个数组,数组中的元素为int*,即整型指针。 int i = 0; for (i = 0; i < 3; i++) { int j = 0; for (j = 0; j < 5; j++) { //printf("%d ", *(arr[i] + j));//arr[i]是指针数组第一个元素储存的地址即a数组首元素的地址,(arr[i]+j)是a数组中第j个元素的地址 printf("%d ", arr[i][j]);//等价于二维数组 } printf("\n"); } return 0; } 数组指针: 如: #include <stdio.h> int main() { int a = 10; int* pa = &a;//整型指针 char ch = 'w'; char* pch = &ch;//字符指针 int arr[10] = { 0 }; int (*parr)[10] = &arr;//取出的是数组arr的地址,不是数组首元素的地址。先是是一个指针,再指向具有10个元素的数组,数组中的元素为int。 double* b[5];//指针数组 double* (*p)[5] = &b;//数组指针,即指向指针数组的指针。先是一个指针,再指向具有5个元素的数组,数组中的元素为double*。 return 0; } (&数组名)与数组名(首元素的地址)区别: #include <stdio.h> int main() { int arr[10] = { 0 }; printf("%p\n", arr); printf("%p\n", &arr);//输出arr与&arr地址的值相同,意义不同(类型不同)。 // int* pa = arr;//类型为int int(*pb)[10] = &arr;//类型为int [10]或int []。 printf("%p\n", pa);//指针加一跳过指向对象的大小。 printf("%p\n", pa+1);//指针加一跳过一个int大小,为4字节。 printf("%p\n", pb); printf("%p\n", pb+1);//指针加一跳过一个int [10]大小,为40字节。 return 0; } 数组名是首元素的地址,但有两个例外: 1:sizeof(数组名) - 数组名表示整个数组,计算的是整个数组大小,单位是字节。 2:&数组名 - 数组名表示整个数组,取出的是整个数组的地址。 数组指针的使用:数组指针中存放数组的地址 如:用于一维数组,一般不使用数组指针,使用一级指针就可以了,使用数组指针反而麻烦。 #include <stdio.h> int main() { int arr[5] = { 1,2,3,4,5 }; int(*pa)[5] = &arr;//指向含有五个整型元素数组的指针,储存的是整个数组的地址 for (int i = 0; i < 5; i++) { printf("%d " ,*((*pa) + i)); //首先*pa是数组arr,而数组arr是数组首元素的地址,故*pa是数组首元素的地址 //接着*pa+j是数组中下标为j的元素的地址,*((*pa)+j)是下标为j的元素 } return 0; } 用于二维数组 #include <stdio.h> //void print(int arr[3][4], int m, int n) {//常规写法 // for (int i = 0; i < m; i++) { // for (int j = 0; j < n; j++) { // printf("%d ", arr[i][j]); // } // printf("\n"); // } void print2(int(*pa)[4],int m, int n) {//定义了指向一维数组的指针, for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { printf("%d ", *(*(pa + i) + j)); //*(pa+i)首先是二维数组第i-1行的地址,*(pa+i)+j是第i-1行的第j-1个元素的地址 //*(*(pa+i)+j)是第i-1行的第j-1个元素 } printf("\n"); } } int main() { int arr[3][4] = { {1,2,3,0},{4,5,6,0},{7,8,9,0} }; //print1(arr, 3, 4); print2(arr, 3, 4); return 0; } int arr[5];//一维整型数组 int a[3][4];//二维整型数组 int* parr1[10];//指针数组,该指针指向存放10个整型指针的数组 int(*parr2)[10];//数组指针,指向存放10个整型元素的数组 int(*parr3[10])[5];//存放10个数组指针的数组,每个数组指针指向存放5个整型变量的数组 函数参数传递方式:按值传递和按引用传递 C中均为按值传递:被调函数的形参为主调函数的副本(拷贝),一般不影响主调函数变量的值。当传入指针时(类似按引用传递),主调函数 向被调函数传入了地址,可以改变主调函数中的相应变量的值。 如:void swap(int *p, int *q)//函数定义 int a=5, b=6; swap(&a, &b);//函数调用
数组参数与指针参数 一维数组传参: #include <stdio.h> void test1(int arr[10]) {//1一维数组接收,可以 } void test1(int arr[]) {//1一维数组接收,可以 } void test1(int* arr) {//1一级指针接收可以 } void test2(int* pa[10]) {//2一维指针数组接收,可以 } void test2(int* pa[]) {//2一维指针数组接收,可以 } void test2(int** pa) {//2可以 } int main() { int arr1[10] = {0}; int* arr2[20] = {0}; test1(arr1);//传的是首元素的地址,此指第一个元素int的地址 test2(arr2);//此指第一个元素int*的地址,即是二级指针 return 0; } 二维数组传参: #include <stdio.h> void test3(int arr[3][4]) {//3二维数组接收,可以 } void test3(int arr[][4]) {//3二维数组接收,可以 } void test3(int arr[][]) {//3二维数组接收,列元素必须写,不可以 } void test3(int* arr) {//一级指针接收,类型不匹配,不可以 } void test3(int* arr[4]) {//一维指针数组接收,类型不匹配,不可以 } void test3(int(*pa)[4]) {//一个数组指针接收,可以 } void test3(int** arr) {//二级指针接收,与一维数组的地址不匹配,不可以 } int main() { int arr[3][4] = { 0 }; test3(arr);//传的是二维数组的首元素的地址,此指第一个元素一维数组的地址 return 0; }
函数指针 指针函数 ****************************************************************************************************************************************************************************************************************
结构体(构造类型) struct关键字用于结构体定义。 结构是同一名字下的一组相关变量的集合。可以包含不同的数据类型。(当然包括结构类型) 结构体定义(声明)形式: struct 结构标识符{ 如:struct student{ 数据类型 成员1; char name[20]; 数据类型 成员2; int age; ...... double mathscore; }; }; 定义之后,struct student便成为了自己定义的新的一种数据类型,与char、int、float、double使用基本相同。编译器不对数据类型分配内存, 当定义了相应类型的变量时编译器会为该类型的变量分配内存。自定义结构类型也是这样。 typedef关键字:为任意数据类型起一个别名,便于使用和理解。 关于结构体使用: 如:typedef struct student STU,即STU成为struct student 数据类型的另一个名字。 或如:typedef struct student{ char name[20]; int age; double mathscore; }STU;此STU不是struct student类型的结构变量,而是struct student 数据类型的另一个名字。 结构变量的声明(定义): a.先声明结构类型,在定义结构变量。 如:struct student a; b.定义结构类型的同时,定义结构变量。(此时结构标识符可以省略,且只能在此处定义结构变量,少使用) 如:struct student{ char name[20]; int age; double mathscore; }a; 结构变量的内存用sizeof运算符计算出,一般大于结构成员所占字节的和。 结构类型可以作为另一个结构的成员: struct date{ int year; int month; int day; } struct student{ char name[20]; int age; double mathscore; struct date born; }; 结构变量的初始化: 与普通变量一样,不同成员用逗号隔开,字符串用双引号,单个字符用单引号。 如:struct student a = { "weihe", 20, 90}; 结构成员的访问方法:C规定结构变量不能整体进行输入输出比较等操作,只能对具体的成员进行输入输出比较等操作。 相同类型的结构变量可以直接进行赋值操作,等价于结构变量成员依次进行赋值操作。 使用成员选择运算符(圆点运算符): struct date{ int year; int month; int day; } struct student{ char name[20]; int age; double mathscore; struct date born; }; struct student a = { "weihe", 20, 90, {2000,4,24}}; a.name == "weihe";//成员引用 a.age == 20; a.date.year == 2000;//嵌套结构体的级联引用 结构数组:数组中元素均为结构类型。(定义和初始化)与普通数组相同。 结构指针:指向结构的指针。 成员访问运算符对于指针:圆点运算符和箭头运算符 如:struct student{ char name[20]; int age; double mathscore; }; struct student a[20]={ {"weihe",20,90}, {"齐天大圣",1000,95}};//初始化,未初始化的赋值为0。 struct student b; a[4].age = 20;//对结构数组第5个元素中的一个变量赋值(引用)。 struct student *p = NULL;//定义结构指针。 p = &b;//得到结构类型b的地址用用取地址运算符。 p = a;//数组名就是数组的地址故不需要取地址运算符,等价于 p = &a[0];. (*p).mathscore = 89;//同时使用解引用运算符与圆点运算符,等价于a[0].mathscore = 89; *(p+1).mathscore = 79;//同时使用解引用运算符与圆点运算符,等价于a[1].mathscore = 79;,指针+1则跳过相应类型的一个元素,再此为一个结构元素。 p->mathscore = 99;//箭头运算符,等价于 a[0].mathscore = 99; 当p定义为指向结构类型的指针时就只能指向定义的类型了,不能指向结构类型中的成员, 但可以通过成员访问运算符(圆点或箭头)访问成员。对于结构指针有两种(圆点和箭头),对于结构名只有圆点运算符。 结构与函数: struct student{ char name[20]; int age; double mathscore; }; void max(int n, struct student a);//结构作为函数的参数,是按值传递,在max函数新建一个结构体(形参),不影响main函数里传参的的结构体(实参)。不常用。 void max(int n,struct student *a);//结构指针作函数参数,传入的是main函数里实参(再此为结构体)的地址,影响实参数值。常用。 void max(int n, struct student a[]);//结构数组作函数参数,与结构指针作用相同,传入的是数组的首元素字节的首地址。 struct student max(int n);//结构作函数返回值,max函数最终返回一个结构(其中一般包含多个结构成员,可一次性返回),类似返回其他类型数据。 联合:C语言中多个不同变量共享同一内存区的功能。 关键字:union 定义:(类比结构) union student {//该联合的三个成员共享一个声明联合的内存单元,须有,同一时间内有且只有一个成员有意义。不能为联合的所有成员都初始化,只能用相应类型的常量为第一个成员初始化。 int n;//4字节 double a;//8字节 char ch;//1字节 }u;//定义的同时可以声明一个联合u。 union student u1;//也可在定义之后再声明一个联合u1。 在为声明的一个联合u分配内存大小时,编译器按成员中最长的类型double分配储存单元。 联合指针: union student { int n;//4字节 double a;//8字节 char ch;//1字节 }u,u1; union student *p = NULL; p = &u; u1.n = p->n;//p->n等价于*p.n。 类比结构。圆点运算符与箭头运算符。 联合与结构:结构可以是联合的成员,联合也可以是结构的成员,能互相嵌套。 如: struct yes{//有对象问名字与年龄 name[20]; int age; }; struct no {//无对象问希望什么时候找与喜欢类型 int hopeseektime; char liketype[50]; }; union otherhalf{定义一个联合是否有对象,成员为两个结构分别表示有或没有 struct yes a; struct no b; }; struct student {//定义学生的结构,包括姓名、年龄与有无对象。 char name[20]; int age; union otherhalf; }; 枚举:一种值由程序员列出的类型,而且程序员必须为每个值命名(枚举常量)。 关键字:enum(类比结构) 在枚举类型声明语句中,花括号内的标识符都是整型常量,称为枚举常量。一般第一个枚举常量是0,依次增加1。 如: enum weekday{ Sun;//0 Mon;//1 Tue; Wed; Thu; Fri; Sat;//6...... }someday0;//定义枚举类型同时声明枚举变量someday0。 enum weekday someday;//先定义枚举类型,再声明枚举变量someday someday = Mon;//枚举变量someday被定义的枚举常量赋值。 time()和localtime() ****************************************************************************************************************************************************************************************************************
int a[10];//数组定义 void array(int a[]) 等价于void array(int *a)//函数定义
动态内存分配 void *malloc(unsigned size)//函数原型
int *p = NULL; p = (int*)malloc(sizeof(int));//使用,按字节分配内存,用于数组个数不定时
动态内存分配之后的释放函数 void free(void *p)//函数原型
int *p = NULL; p = (int*)malloc(sizeof(int)); free(p);//使用
****************************************************************************************************************************************************************************************************************
void *calloc(unsigned n,unsigned size)//函数原型 为n个数组分配size字节大小的内存,并均初始化为0
****************************************************************************************************************************************************************************************************************
void realloc(void* p,unsigned size);//函数原型 重新为指针p所指的内存块分配适合的内存 若指针p指向NULL,则作用与malloc()相同
**************************************************************************************************************************************************************************************************************** EOF(End OF File) 表示文件结束符,用于while循环中,如while(scanf("%d",&n) != EOF),当在控制台输入ctrl+z时结束程序进行
****************************************************************************************************************************************************************************************************************
数据的存储 程序的版本:Debug版本和Release版本 Debug版本即调试版本,包含调试信息,利于去调试程序,无优化。 Release版本即发布版本,对程序进行了多种优化如代码大小和运行速度,便于使用,无调试信息。 数据类型: 类型的意义:确定开辟空间的大小与看待内存空间的视角。 基本类型: char 字符型//char C语言没有规定是signed char 还是unsigned char 类型,由具体的编译器决定,多数编译器默认为 signed char类型 short 短整型 int 整型//int C语言规定为signed int类型 long 长整型 long long 更长的整型 float 单精度浮点型 double 双精度浮点型 整形家族: char unsigned char 0~255(2^8-1) signed char -128~127 short unsigned short int signed short int int unsigned int 0~4,294,967,295(2^32-1) signed int(默认) -2,147,483,648~2,147,483,647 long unsigned long int signed long int 浮点家族: float double 构造类型(自定义类型): 数组类型 a[] 结构体类型 struct 枚举类型 enum 联合类型 union 指针类型: int *p float *p double *p char *p void *p 空类型:函数返回,函数参数,指针 数据的截断与整型提升(指不同的数据类型之间 ): 如:int a = 10; char ch = 10;//10的二进制:00000000 00000000 00000000 00001010 最高位为0,原码反码补码相同 10为整型,占内存的四个字节,由字符类型(一个字节)ch储存时发生截断,只保留最后一个字节(00001010)储存。 ch转化为整型时从前补24个最高位即0,得补码00000000 00000000 00000000 00001010,最高位为0原码反码补码相同,即得原码00000000 00000000 00000000 00001010即10。 堆栈 局部变量储存在栈区,栈区地址从下到上逐渐增大,即上为高地址,下为低地址。栈区先使用高地址,在使用低地址 整型在内存中的储存: 已知数据在内存中以二进制储存, 有符号数的二进制表示方式有三种:原码,反码,补码。,每一种均分为符号位和数值位。 对于正整数:原码,反码,补码三者相同。 对于负整数: 数据储存时的二进制序列最高位为1,而正整数二进制序列最高位为0。 对于signed前缀类型,最高位为符号位无数值表示,而unsigned前缀类型最高位为数值表示。 原码:由数据的数值直接写出的二进制序列,补码取反再加一又得到原码或补码减去1再取反得到原码。 反码:原码符号位(即1)不变,其它位按位取反。 补码:先取反码再+1得到。 如 int a = -10; 内存中存放(16进制): F6 FF FF FF(此储存为小端字节序) 原码:10000000 00000000 00000000 00001010(四个字节) 反码:11111111 11111111 11111111 11110101 补码:11111111 11111111 11111111 11110110 16进制:FF FF FF F6 int b = 10 内存中存放(16进制):0a 00 00 00(此储存为小端字节序) 原码:00000000 00000000 00000000 00001010 反码:00000000 00000000 00000000 00001010 补码:00000000 00000000 00000000 00001010 16进制:00 00 00 0a 整型在内存中以补码的形式储存,表示和计算,原因是可以将符号位和数值域统一处理,加法和减法可以统一处理(cpu中只有加法器),原码与补码相互转换运算过程相同,不需要额外硬件电路。 数据数值的二进制的储存形式:大端字节序和小端字节序 如 int a = 0x11223344;//(16进制表示)其中11为高位,44为低位,类比十进制数字 大端字节序:把数据的低位字节序的内容存放在高地址处,高位字节序的内容存放在低地址处。 低地址 11 22 33 44 高地址 小端字节序:把数据的地位字节序的内容存放在低地址处,高位字节序的内容存放在高地址处。 低地址 44 33 22 11 高地址 想知道自己计算机数据的储存形式,可以采用如下代码简单判断: 用字符指针(只看一个字节)指向整型a的第一个字节,若为小端则为(16进制)01 00 00 00,若为大端则为 00 00 00 01。 #include <stdio.h>
int main(){ int a=1; char *p=(char*)&a; if(*p==1) printf("小端\n"); else printf("大端\n"); return 0; } 浮点型在内存中的存储:float(四字节),double(八字节),long double 浮点数在内存中以补码的形式储存。 C语言用二进制储存浮点数的标准:IEEE 754。 任意一个二进制浮点数V均可以化为一个形式: (-1)^S*M*2^E 其中 ^ 表示指数运算。 S表示符号位,S为0时V为正数,S为1时V为负数。 M表示有效数字,范围大于等于1,小于2。 2^E表示指数位。 例如:9.0(十进制)->1001.0(二进制)->1.001*2^3->0 10000010 00100000000000000000000 5.5(十进制)->101.1(二进制)->1.011*2^2-> 0 10000001 01100000000000000000000 对于double类型:0 00000000000 0000000000000000000000000000000000000000000000000000 首位为符号位,接下来11位存放指数,余下52位存放小数部分。 对于float而言共四个字节32位:0 00000000 00000000000000000000000 首位1或0表示符号位,接着8位存放E即指数(二进制形式),余下的23位储存M(又称小数部分),M不够就补0. 对于1.XXXXXXXXX的二进制有效数字,实际储存时舍去1和小数点,只保留小数点后的0和1,等到取出已经储存好的浮点数时,M部分取出时在开头添加上1和小数点。 对于E指数,看做无符号整型(unsigned int),float中占8位,范围为0~255,考虑到指数E可能为负数,实际储存时要存入正数就需要修正,为指数E加上一个中间值, 127(float)或1023(double)。实际储存的是(E+127)的二进制形式,double占11位范围为0~2047,实际储存的是(E+1023)的二进制形式。 取出时: E全为0时: 这时浮点数的指数E等于(1-127)或(1-1023)作为真实值,有效数字M不在加上第一位的1,而是还原为0.xxxxxx的小数,以此表示正负0或接近于0很小的数。 00000000八个位为0 ->实际的E的值为0 - 127 == -127,2^(-127)是很小的一个数,正负接近于0。 E全为1时: 这时,如果有效数字M全为0(还原之后为1.000.....),表示无穷大,符号确定正负。 11111111八个位为1 -> 实际的值为255 - 127 == 128,2^(128)是很大的数字 ,表示无穷大。 E不为全0或全1时:(最多) 指数E二进制的计算值减去127(或1023)得到指数位真实值,有效数字M前加上第一位的1, 5.5(十进制)->101.1(二进制)->1.011*2^2-> 0 10000001 01100000000000000000000 ***************************************************************************************************************************************************************************************************************
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有