家纺 网站模版,城市门户网站,做网站襄樊,常州做网站一般多少钱C语言学习
五.操作符
5.单目操作符(2)
sizeof不能用于计算动态分配的内存 在对数组使用sizeof时#xff0c;返回的是整个数组的大小#xff08;所有元素的总字节数#xff09;。而对指针使用sizeof时#xff0c;返回的是指针本身的大小#xff08;通常是机器字长的大小…C语言学习
五.操作符
5.单目操作符(2)
sizeof不能用于计算动态分配的内存 在对数组使用sizeof时返回的是整个数组的大小所有元素的总字节数。而对指针使用sizeof时返回的是指针本身的大小通常是机器字长的大小
6.关系操作符 !
7.逻辑操作符
逻辑与 ||逻辑或 i a b c其中如果a为0则后面的代码都不会执行 i a || b || c其中如果a为1则后面的代码都不会执行
8.三目操作符
a b ? a : b 若ab则取a若不大于则取b
9.逗号表达式
逗号表达式就是用逗号隔开的多个表达式逗号表达式从左向右依次执行整个表达式的结果是最后一个表达式的结果
10.下标引用、函数调用、结构成员
下标引用arr[x] 函数调用getMax(a, b) 结构成员obj.name或者obj-name
11.表达式求值
表达式求值的顺序一部分是由操作符的优先级和结合性决定 同样有些表达式的操作数在求值的过程中可能需要转换为其它类型 隐式类型转换c的整型算数运算总是至少以缺省整型类型的精度来进行的 为了获得这个精度表达式中的字符和短整型操作数在使用之前被转换为普通整型这叫整型提升 整型提升的意义避免数据丢失或溢出同时可以保证运算结果的准确性 char类型赋值为整型时会默认是ASCII码并转换 代码实例
第二种隐式类型转换算术转换如果某个操作符的各个操作数属于不同的类型那么除非其中一个操作数的转换为另一个操作数的类型否则操作就无法进行
12.操作符的属性
复杂表达式的求值有三个影响的因素
操作符的优先级操作符的结合性是否控制求值顺序
i i-- - --i是一个有问题的表达式不同的编译器会计算出不同的结果
六.初始指针
1.指针
指针是编程语言中的对象利用地址它的值指向存储在电脑中的某处内存中的值 获取指针int* p a; 指针就是变量用来存放地址的变量 一个小的内存单元是一个字节
2指针类型
指针类型决定了指针进行解引用操作的时候能够访问空间的大小 int* p; *p能访问4个字节 char* p; *p能访问1个字节 double* p; *p能访问8个字节 指针类型还决定了指针的步长 int* p; p1向后走4个字节 char* p; p1向后走1个字节 double* p; p1向后走8个字节
3.指针与数组
指针可以直接修改数组中的数据
通常数组名就是首元素地址也可能是整个数组
4.野指针
野指针就是指针指向的位置是不可知的 原因
指针未初始化默认是随机值指针越界访问指针超过数组的长度时它会变为随机值指针指向的内存空间被释放
注意全局指针指向局部变量或主程序中的指针指向函数中的变量时会报警告 指针最好初始化可以int* p NULL;
5.指针运算
①指针±整数 ②指针-指针不同类型的指针最好不要相减 ③指针的关系运算标准规定允许指向数组元素的指针与指向数组最后一个元素的后一位的地址比较但是不允许与指向第一个元素之前的那个内存位置的指针进行比较所以不是所有编译器允许这种运算
6.二级指针
int a 10;
int* pa a;
int** ppa pa其中ppa就是二级指针**ppa10;
7.指针数组
定义指针数组int* arr[3] {a, b, c}; 输出指针数组指向的数据
七.实用调试技巧
1.调试的步骤
发现程序错误的存在以隔离、消除等方式对错误进行定位确定错误产生的原因提出纠正错误的解决办法对程序错误予以改正重新测试
system(“pause”);语句可以让代码在该语句中终止
2.debug和release
debug通常为调试版本包含一些调试信息可被调试文件较大 release通常为发布版本代码会被自动优化
注意C语言中并没有内建的越界检查因此开发者需要小心确保不要越界访问数组 assert头文件中的assert函数断言当条件不符合时直接报错 代码实例
对于常量可以用改地址的对应的值来改值所以一般对常量的地址也用const修饰 区分const int* p(*p是常量)和int* const p(p是常量)
八.结构体
1.结构体的声明
struct tag
{member-list;
}全局的结构体变量;结构体是一些值的集合这些值可以是不同类型
2.结构体的初始化
①按顺序初始化struct Person person1 {“Alice”, 25}; ②指定成员初始化struct Person person2 {.age 30, .name “Bob”}; ③嵌套结构体的初始化struct Student student1 {“David”, {5, 8, 1999}}; ④动态分配并初始化
struct Person *person3 malloc(sizeof(struct Person));
if (person3 ! NULL) {person3-age 22;strcpy(person3-name, Charlie);
}3.结构体传参
两种结构体传参方式把结构体整个传过去把结构体的地址传过去 把结构体整个传过去压栈压栈通常指的是将数据存储到栈stack数据结构中的操作消耗的内存过大 结论建议把结构体的地址传过去
九.数据的存储
1.数据类型
整型家族 charunsigned char、signed charASCII码值所以归类为整型 shortunsigned short、signed short intunsigned int、signed int longunsigned long、signed long 浮点型家族 float double long double 构造类型 数组类型 结构体类型 struct 枚举类型 enum 联合类型 union 指针类型 int* char* float* void* 空类型void无类型
2.整型在内存中的存储
计算机中的有符号数有三种表示方法即原码、反码、补码 三种表示方法均有符号位和数值位符号位为0则是正数 有符号数的二进制序列原码 原码取反除外符号位反码 反码1补码 正数的原、反、补码都相同 对于整型数据存放在内存中的是补码 大端存储模式是指数据的低位保存在内存的高地址中而数据的高位保存在内存的低地址中 小端存储模式是指数据的低位保存在内存的低地址中而数据的高位保存在内存的高地址中 为什么有大端小端寄存器宽度大于一个字节那么必然存在着多个字节安排的问题所以有大端小端的存储模式 判断当前机器的字节序的小程序
注解指针类型决定了指针解引用操作符能访问几个字节char*访问1个字节int*访问4个字节
printf格式控制符
%d用于输出整型 %c用于输出单个字符 %s用于输出字符串 %f用于输出浮点数 %u用于输出无符号整数 %x用于输出十六进制数 %o用于输出八进制数 %p用于输出指针地址
3.浮点数在内存中的存储
IEEE754规定任意二进制浮点数V可表示为-1s*M*2E 其中
-1^s表示符号位当s0V为正数M表示有效数字2^E表示指数位
IEEE754规定对于32位的浮点数单精度最高的1位是位s接着的8位是指数E剩下的23位是数字位 对于64位的浮点数双精度最高的1位是位s接着的11位是E剩下的52位是数字位 对于有效数字MM可以写成1.xxx的形式其中xxxx表示小数部分 IEEE754规定在计算机内部保存时默认这个数的第一位总是1因此可以省去 对于指数EE是一个无符号整数但是科学计数法中的E可能出现负数所以IEEE754规定存入内存时E的真实值必须再加上一个中间数对于8位的E这个中间数是127对于11位的E这个中间数是1023比如2^10的E是10所以保存为137即10001001 例子 float 5.5 - 101.1 --10*1.011*22 - S0, M1.011, E2 - 01000000101100000000000000000000 - 0x40b00000 E取出来的三种情况
E不全为0且不全为1先减127(或1023)得到真实值再将有效数字M前加上第一位的1E全为0E等于1-127即为真实值M不再加上第一位的1因为它本身就很接近0E全为1如果有效数字M全为0表示±无穷大正负取决于符号位s
十.指针详解
1.字符指针
字符指针char* 一般使用
char ch w;
char* pc ch;
*pc a;非常规使用
char *str Hello, world!;在这种情况下编译器会自动为字符串分配内存并将其存储在程序的静态存储区域。因此str 指向的是字符串的第一个字符的地址。 请注意如果您尝试修改 str 指向的字符串可能会导致未定义的行为。这是因为在大多数情况下字符串存储在只读内存区域因此尝试修改字符串的内容可能会导致程序崩溃或产生其他不可预测的结果。如果您需要修改字符串应该使用字符数组而不是字符指针来存储它建议用const修饰该str
2.指针数组
指针组成的数组叫指针数组 一般用法
int arr1[] {1, 2, 3};
int arr2[] {2, 3, 4};
int arr3[] {3, 4, 5};
int* parr[] {arr1, arr2, arr3}3.数组指针
数组指针是一个指针 定义一个数组指针int(*p)[10] arr;其中(*p)表示p是指针 数组指针的使用一般数组指针用在二维以上数组
int main(){int arr[3][5]{ {1, 2, 3, 4, 5}, {2, 3, 4, 5, 6}, {3, 4, 5, 6, 7} };print2DArray(arr, 3, 5);return 0;
}
void print2DArray(int (*arr)[5], int rows, int cols) {for (int i 0; i rows; i) {for (int j 0; j cols; j) {printf(%d , arr[i][j]);}printf(\n);}
}指针数组和数组指针的比较
int arr[5]; arr是一个5个元素的整形数组 int *parr1[5]; arr1是一个数组数组有10个元素每个元素的类型是int*parr1是指针数组 int (*parr2)[10]; parr2是一个指针该指针指向了一个数组数组有10个元素每个元素的类型是intparr2是数组指针 int (*parr3[10])[5]; parr3是一个数组该数组有10个元素每个元素是一个数组指针该数组指针指向的数组有5个元素每个元素的类型是int
数组传参 以上方法均ok
上述方法中下四个方法中只有数组指针的那个是OK的 一级指针传参传的可以是数组名取地址、一级指针 二级指针传参传的可以是二级指针、一级指针取地址
4.函数指针
函数指针是指向函数的指针 定义一个函数指针int (*pa)(int, int) Add; int是函数的返回类型pa是指针名(int, int)是函数的传参列表Add是函数名 函数名和函数名本身都是函数的地址