莱芜信息港房产,网站推广优化技巧大全,一般网站 广告,南昌网站搭建制作公司✨✨ 欢迎大家来到小伞的大讲堂✨✨ #x1f388;#x1f388;养成好习惯#xff0c;先赞后看哦~#x1f388;#x1f388; 所属专栏#xff1a;C语言详解 小伞的主页#xff1a;xiaosan_blog gitee:许星让 (xu-xingrang) - Gitee.com 制作不易#xff01;点个赞吧 if ((char*)i) { printf(小端); } else { //数据的低位保存在内存的高地址中 printf(大端); } } void check_endian() { union { int i; char c; }un; //使用联合体共用一份资源 //0000设i的值为1 //低 高 //1000 //char取低位的值为1为低端为0则高端 un.i 1; if (un.c 1) { printf(小端); } else { printf(大端); } }1.3 整形不同类型间互相赋值时截断和提升问题1.3.1 整形截断Integer Truncation整形截断是指在将一个较大范围的数值类型如浮点数或大范围的整数类型转换为较小范围的整数类型时数据的高位部分被丢弃只保留低位的部分。这通常发生在强制类型转换时导致数据的精度丧失。整形截断的例子类似于浮点数向下取整考虑将一个浮点数转换为整数类型或将一个大范围整数类型转换为较小的整数类型。小数部分或高位部分会被截断导致数据丢失。#include iostream int main() { double pi 3.14159; int truncatedPi (int)pi; // 强制将 double 转换为 int std::cout Truncated Pi: truncatedPi std::endl; // 输出 3 return 0; }1.3.2 整形提升Integer Promotion整形提升是指在运算过程中较小的整型数值会被自动提升为较大的整型数值。这种行为通常发生在参与算术运算时C/C编译器会根据整型提升规则自动将较小的整数类型提升为 int 类型或更大的数据类型。整形提升规则确保算术运算中的精度不会丢失并且避免了低位类型之间的溢出。整形提升的规则C/C 中的整形提升规则如下char 和 short 提升为 int如果一个char 或 short 类型的数值参与算术运算它会被提升为 int 类型。这是因为 int 足够大能够容纳 char 和 short 类型的数值范围。如果 int 不足以容纳数值提升为 long 或更高的数据类型如果计算涉及到的数据超出了 int 类型的范围例如使用 64 位数值则会提升为 long 或 long long 类型。不同类型的算术运算会将结果提升为较大的类型在表达式中如果参与运算的数值类型不同C/C 会将较小的类型提升为较大的类型通常会提升到 int 或 long。#include iostream int main() { char a 5; short b 10; int result a b; // a 和 b 都会被提升为 int 类型 std::cout Result of a b: result std::endl; // 输出 15 return 0; }整形提升的实际应用整形提升常用于以下几种情况算术运算整形提升使得算术运算如加法、减法、乘法、除法等过程中较小的数据类型被自动提升为 int 类型保证了计算的精度和正确性。例如char 和 short 被提升为 int 后进行加法运算可以避免加法时发生溢出。混合类型运算当一个表达式中涉及到不同大小的整型时C/C 会自动进行类型提升以确保计算结果的准确性。例如char 和 int 相加时char 会被提升为 int结果也会是 int 类型。1.3.3 整型提升与标准操作符例如在 C 中默认情况下所有整数运算包括加法、减法、乘法等都将小整数类型如char和short提升为int#include iostream int main() { char c 10; short s 20; int result c * s; // c 和 s 被提升为 int运算结果是 int 类型 std::cout Multiplication result: result std::endl; // 输出 200 return 0; }在上面的代码中c是char类型s是short类型在执行乘法运算时它们都被提升为int类型进行计算确保结果不会丢失精度。概念整型截断整型提升定义数据从较大类型转换到较小类型时高位数据被丢弃。较小类型的数据在算术运算中自动提升为较大的类型。常见场景浮点数转换为整数较大整数类型转换为较小类型。char和short类型自动提升为int。影响可能导致数据丢失、精度丧失或溢出。保证算术运算精度不会发生数据溢出。例子将double转换为int小数部分被截断。char与short在加法运算中提升为int类型。1.3.4 总结整型截断是当较大类型转换为较小类型时数据的高位部分被丢弃。它可能会导致数据丢失或溢出开发者需要小心使用。整型提升是在算术运算中较小的整数类型会被提升为 int 类型或更大的类型避免了精度丧失或溢出保证了计算结果的正确性。理解这些概念尤其是在进行类型转换和数值计算时可以帮助开发者更好地处理数据类型避免错误和不必要的性能问题。1.2 浮点数的存储常见的浮点数3.14159、1E10等浮点数家族包括 float、double、long double 类 型。浮点数表示的范围float.h中定义#include stdio.h int main() { int n 9; float* pFloat (float*)n; printf(n的值为%d\n, n); printf(*pFloat的值为%f\n, *pFloat); *pFloat 9.0; printf(n的值为%d\n, n); printf(*pFloat的值为%f\n, *pFloat); return 0; } n的值为9 *pFloat的值为0.000000 n的值为1091567616 *pFloat的值为9.0000001.2.1 浮点数的存储上面的代码中 n 和 *pFloat 在内存中明明是同一个数为什么浮点数和整数的解读结果会差别这 么大要理解这个结果一定要搞懂浮点数在计算机内部的表示方法。根据国际标准IEEE电气和电子工程协会754任意一个二进制浮点数V可以表示成下面的形式举例来说十进制的5.0写成二进制是 101.0 相当于1.01* 2^ 2 。按照上面V的格式可以得出S0M1.01E2。十进制的-5.0写成二进制是 -101.0 相当于-1.01* 2^ 2 那么S1M1.01E2。IEEE 754规定1对于32位的浮点数(float)最⾼的1位存储符号位S接着的8位存储指数E剩下的23位存储有效数字M2对于64位的浮点数(double)最⾼的1位存储符号位S接着的11位存储指数E剩下的52位存储有效数字M。IEEE 754 对有效数字M和指数E还有⼀些特别规定。前⾯说过1≤M2也就是说M可以写成 1.xxxxxx 的形式其中 xxxxxx 表示小数部分。IEEE 754 规定在计算机内部保存M时默认这个数的第⼀位总是1因此可以被舍去只保存后⾯的xxxxxx部分。比如保存1.01的时候只保存01等到读取的时候再把第一位的1加上去。这样做的目的是节省1位有效数字。以32位浮点数为例留给M只有23位将第一位的1舍去后等于可以保存24位有效数字。至于指数E情况就比较复杂⾸先E为一个无符号整数unsigned int这意味着如果E为8位它的取值范围为0~255如果E为11位它的取值范围为0~2047。但是我们知道科学计数法中的E是可以出现负数的所以IEEE 754规定存入内存时E的真实值必须再加上一个中间数对于8位的E这个中间数是127对于11位的E这个中间数是1023。比如2^10的E是10所以保存成32位浮点数时必须保存成10127137即10001001。我们在回看这个题目#include stdio.h int main() { int n 9; float* pFloat (float*)n; printf(n的值为%d\n, n); printf(*pFloat的值为%f\n, *pFloat); *pFloat 9.0; printf(n的值为%d\n, n); printf(*pFloat的值为%f\n, *pFloat); return 0; } 9的二进制为1001.0 按照公式: V (-1)^S * M * 2^E V (-1)^0 * 1.001 * 2^3 按照浮点数的存储模式 S E M 0 00000000 00000000000000000000000 因为1 M 2存储时默认保留小数位 M 1.001所以M为 001 00000000000000000000 E 中间值 32位下的:E127 64位下的:E2047 所以E 127 130 10000010 S E M 0 10000010 00100000000000000000000 进行二进制转十进制为10915676162、数组与指针2.1 什么是指针什么是数组数组和指针的联系和区别本质类型不同数组是一组相同类型元素的集合数组中存放的是1个或者多个数据但是数组元素个数不能为0。数组中存放的多个数据类型是相同的。指针是存储地址的变量指向某个内存位置。1.占用内存不同数组N * sizeof type 连续的内存指针固定大小4或8字节跟随系统2.赋值与修改//数组为常量指针 int arr[5]; arr new int[5]; // 错误arr不能指向其他地址指针为变量 int *p; p arr; // 合法 p arr[2]; // 合法3. 取地址操作的含义数组名arr返回整个数组的地址类型为int(*)[5]值与arr相同但类型不同。printf(%p %p\n, (void*)arr, (void*)arr); // 输出相同地址指针p返回指针变量本身的地址。2.2 什么是数组指针什么是指针数组指针数组是一个数组其元素都是指针数组指针指向一个数组的指针是一个指针#include stdio.h int main() { // 定义三个整型数组 int arr1[] { 1, 2, 3 }; int arr2[] { 4, 5, 6 }; int arr3[] { 7, 8, 9 }; // 定义一个指针数组存储整型数组的地址 int* pArr[3] { arr1, arr2, arr3 }; // 计算每个整型数组的元素之和 for (int i 0; i 3; i) { int sum 0; for (int j 0; j 3; j) { sum pArr[i][j]; } printf(arr%d 的和: %d\n, i 1, sum); } return 0; } arr1 的和: 6 arr2 的和: 15 arr3 的和: 24#include stdio.h int main() { int num1 1, num2 2, num3 3, num4 4, num5 5; // 指针数组//存放指针的数组 int* ptr_array[] { num1, num2, num3, num4, num5 }; // 二维整数数组 int int_array[][3] { {10, 11, 12}, {20, 21, 22}, {30, 31, 32}, }; // 数组指针//指向数组的指针 int (*array_ptr)[3] int_array; return 0; }int (*pB)[10];//就是一个数组指针 在这个示例中我们创建了一个整数指针数组 ptr_array 和一个数组指针 array_ptr ptr_array 是一个整数指针数组该数组包含 5 个整数指针。每个指针指向一个整数变量。 array_ptr 是一个指向二维整数数组的指针。这个二维数组存储了多个整数每一行包含 3 个整数。2.3 数组名和数组名分别代表的意义数组名a数组名可以作为数组第一个元素的指针。我们由数组和指针的关系知道a代表这个地址数值它相当于一个指针指向第一个元素a[0])即指向数组的首地址。数组中的其他元素可以通过a的位移得到此时的进阶是以数组中单个的元素类型为单位的即ai a[i]。数组名取地址a对于一个普通的变量bb是指用取地址符号取得变量b的存放地址a在内存中没有分配空间只对数组a的各个元素分配了存储空间此处数组名字a显然不是普通的变量a也不代表所取a的存储地址。int main() { int a[] { 1,2,3 }; printf( a %p\na %p\n, a, a); printf( sizeof(a) %d\nsizeof(a) %d\n, sizeof(a), sizeof(a)); /* a 000000DE1C6FF6D8 a 000000DE1C6FF6D8 sizeof(a) 12 sizeof(a) 8 */ //sizeof(a) 为整个数组的大小sizeof(a)为指针大小 //a指向a[0]的指针a指向整个数组的指针虽然值相等但是类型上是不同的 return 0; }2.4 什么是二维数组二级指针?1.二维数组可看作多个一维数组C 语言中的二维数组在内存中实际上是按行连续存储的。例如定义一个二维数组int arr[3][4]它在内存中的存储方式就像是一个包含了 3 个长度为 4 的一维数组。这意味着可以将二维数组看作是一个一维数组其中每个元素又是一个一维数组。这种连续存储的特性为通过指针访问二维数组元素提供了基础。数组名含义二维数组名arr可以看作是一个指向数组首元素的指针。对于二维数组首元素是一个包含 4 个int类型元素的一维数组。所以arr的类型是int (*)[4]即指向包含 4 个int类型元素的一维数组的指针。可以通过arr[i]来访问二维数组的第i行这里的arr[i]等价于*(arr i)它表示第i行的首地址而arr[i][j]则是第i行第j列的元素。2.二级指针指针的地址二级指针是指向指针的指针。例如int **p定义了一个二级指针p它可以存储一个指向int类型指针的地址。二级指针在处理复杂的数据结构、动态内存分配以及函数参数传递等方面非常有用。它提供了一种间接访问和操作数据的方式可以通过多次解引用来访问最终的数据。与一维数组相比较一级指针存储的是一个变量的地址通过解引用可以访问该变量的值。而二级指针存储的是一个指针的地址需要两次解引用才能访问到最终的数据。例如对于int *q*q可以访问到一个int类型的值。而对于int **p*p得到一个指向int类型的指针**p才是最终的int类型的值。2.5 什么是函数指针?函数指针其本质是一个指针变量该指针变量指向一个函数。C程序在编译时每一个函数都有一个入口地址该入口地址就是函数指针所指向的地址。函数指针示例#include stdio.h int max(int x, int y) { return x y ? x : y; } int main(void) { // 定义一个函数指针 p指向函数 max //返回值 (参数) int (*p)(int, int) max; int a, b, c, d; printf(请输入三个数字:); scanf(%d %d %d, a, b, c); // 使用函数指针调用函数 d p(p(a, b), c); printf(最大的数字是: %d\n, d); return 0; }2.6 常见的笔试题##一维数组int main() { //一维数组 int a[] { 1,2,3,4 }; printf(%d\n, sizeof(a)); //16:整个数组的大小 printf(%d\n, sizeof(a 0)); //4/8:a为首元素的地址0偏移量a0为指针//char* printf(%d\n, sizeof(*a)); //4:解引用为首元素大小//char printf(%d\n, sizeof(a 1)); //4/8:同第二个//char* printf(%d\n, sizeof(a[1])); //4:元素值//char printf(%d\n, sizeof(a)); //4/8:指针//char* printf(%d\n, sizeof(*a)); //16:a:取地址在解引用//char printf(%d\n, sizeof(a 1)); //4/8:地址偏移还是指针,跳过整个数组//地址 printf(%d\n, sizeof(a[0])); //4/8:指针//char* printf(%d\n, sizeof(a[0] 1)); //4/8:指针偏移//char* /* 16 8 4 8 4 8 16 8 8 8 */ char arr[] abcdef; printf(%d\n, sizeof(arr)); //7:整体大小包括\0 printf(%d\n, sizeof(arr 0)); //4/8:指针//char* printf(%d\n, sizeof(*arr)); //1:char a大小//char printf(%d\n, sizeof(arr[1])); //1:cahr a大小//char printf(%d\n, sizeof(arr)); //4/8:地址//char* printf(%d\n, sizeof(arr 1)); //4/8:跳过整个数组地址大小//char* printf(%d\n, sizeof(arr[0] 1));//4/8:cahr * 1地址//char* printf(%d\n, strlen(arr)); //6 printf(%d\n, strlen(arr 0)); //6 //整数提升//arr char *//arr* char //strlen(a); //printf(%d\n, strlen(*arr)); //报错 //printf(%d\n, strlen(arr[1])); //报错 printf(%d\n, strlen(arr)); //6:虽然大小相同但是与arr类型不同存在强转 printf(%p\n, (arr 1)); //000000597AAFFABB printf(%d\n, strlen(arr 1)); //21 //跳过整个数组//碰巧到21位遇到\0 printf(%d\n, strlen(arr[0] 1));//5 //下标1往后 return 0; }##二维数组int main(){ //二维数组 int a[3][4] { 0 }; printf(%d\n, sizeof(a)); //48 printf(%d\n, sizeof(a[0][0])); //4 printf(%d\n, sizeof(a[0])); //16:下标为0的数组大小a[4] printf(%p\n, (a[0] 1)); //-1517291332,地址值 printf(%d\n, sizeof(a[0] 1)); //4/8:a[1][0]的地址 printf(%d\n, sizeof(*(a[0] 1)));//4 printf(%d\n, **(a 1)); //为0 printf(%d\n, sizeof(a 1)); //4/8:跳过整个二维数组 printf(%d\n, sizeof(*(a 1))); //16:下标为1的数组a[4] printf(%d\n, **(a[0] 1)); //为0 printf(%d\n, sizeof(a[0] 1)); //4/8:指向a[1][0]指针 printf(%d\n, sizeof(*(a[0] 1)));//16 printf(%d\n, sizeof(*a));//16 printf(%d\n, sizeof(a[3]));//16 return 0; }3. C语言常见的库函数3.1 字符串函数3.1.1 strlen函数求字符串的长度遇到\0返回长度头文件#include string.h特殊考点当strlen遇上转义字符求其长度转义字符含义ASCII 值十进制\n换行Newline10\t水平制表符Tab9\r回车Carriage Return13\b退格Backspace8\f换页Form Feed12\v垂直制表符Vertical Tab11\单引号39\双引号34\\反斜杠92\0空字符Null Terminator0\a响铃Alert/Bell7strlen与sizeof的区别strlen遇见\0返回长度sizeof求字符串大小包括\03.2.1 strcpy()函数模型:char * strcpy( char * str1, const char * str2 );参数:目标字符串和被拷贝字符串字符串拷贝将字符串str2的内容拷贝到字符串str1中#include stdio.h #include string.h int main() { char str1[20]; char str2[] abcdef; strcpy(str1, str2);//将字符串str2的内容拷贝到字符串str1中 printf(%s, str1); return 0; }strcpy()函数在使用时目标字符串的空间必须足够大比被拷贝的字符串空间大因为strcpy是当遇到’\0’是才会停止拷贝这样才能正常使用如果空间太小就无法使用因此在有些编译器比如VS中会认为该函数不安全但是在C语言的库中还有另一个字符串拷贝函数----strncpy()相比于strcpy()函数更安全3.3.1 strcat()函数模型:char * strcat( char * str1, const char * str2 );将字符串str2追加到字符串str1后面strcat()函数注意事项与strcpy()函数一样strcat()同样存在不安全的情况当strcat()追加自己时就无法完成3.4.1 strstr()函数模型:char * strstr( const char * string, const char * strCharSet );字符串查找函数如果字符串strCharSet在字符串string中返回第一次出现的位置的指针若不在string中则返回空指针3.2内存函数3.2.1 memcpyvoid *memcpy(void *dst, void const *src, size_t length);函数memcpy从source的位置开始向后复制num个字节的数据到destination指向的内存位置。这个函数在遇到 \0 的时候并不会停下来。如果source和destination有任何的重叠复制的结果都是未定义的。对于重叠的内存交给memmove来处理。 memcpy函数的模拟实现:void * memcpy ( void * dst, const void * src, size_t count) { void * ret dst; assert(dst); assert(src); /* * copy from lower addresses to higher addresses */ while (count--) { *(char *)dst *(char *)src; dst (char *)dst 1; src (char *)src 1; } return(ret); }3.3.1 memmovevoid * memmove ( void * destination, const void * source, size_t num );和memcpy的差别就是memmove函数处理的源内存块和⽬标内存块是可以重叠的。如果源空间和⽬标空间出现重叠就得使⽤memmove函数处理。3.4.1 memsetvoid * memset ( void * ptr, int value, size_t num );##memset是⽤来设置内存的将内存中的值以字节为单位设置成想要的内容。 #include stdio.h #include string.h int main () { char str[] hello world; memset (str,x,6); printf(str); return 0; }3.5.1 memcpyvoid *memcpy(void *str1, const void *str2, size_t n)str1 -- 指向用于存储复制内容的目标数组类型强制转换为 void* 指针。str2 -- 指向要复制的数据源类型强制转换为 void* 指针。n -- 要被复制的字节数。3.6 整形和字符串转换函数3.6.1 atoi函数1.使用atoi函数需要包含stdlib.h头文件2.当参数位置传入的为NULL指针时会报错当参数位置传入的为空字符串时返回03.atoi函数会自动过滤开始时的空白字符4.atoi函数会根据字符串内容自动判断整数的正负5.当字符串的数字大小超过了整型数字的取值范围时返回时会变为整型数据的最大或最小值6.当字符串中的数字有非10进制字符隔断时atoi函数会直接返回当前位置前面的数字3.6.2 itoa()函数char *itoa(int value, char *string, int radix);4.结构体枚举联合4.1 结构体整体的大小在满足为最大数据类型所占字节的倍数下要达到所占内存最小。最大对起数为4从上到下c1占一字节剩下字节不足4字节浪费3字节c2继续占有1字节但要满足4字节4.2 enumenum 枚举名 {枚举常量1, 枚举常量2, ..., 枚举常量n};enum Month {Jan 1, Feb, Mar, Apr, May, Jun,Jul, Aug, Sep, Oct, Nov, Dec};##当指定Jan为1后面默认一次向后14.3 union联合体中最大成员所占内存的大小且必须为最大类型所占字节的最小倍数。5.内存管理malloc和calloc和realloc的区别malloc开辟未初始化的连续内存块num_bytes的大小其值为不确定void *malloc(unsigned int num_bytes)calloc开辟n个size的大小其值为0void *calloc(size_t n, size_t size)realloc开辟size大小的空间成功返回指向新内存块的指针void* realloc(void* ptr, size_t size);特性malloccallocrealloc作用分配指定大小的内存块分配多个元素并初始化为0调整已分配内存块的大小是否初始化否内存内容是未定义的随机值是全部初始化为0否保留原有内容参数单个参数总字节数两个参数元素个数 × 单个元素大小两个参数原指针 新大小返回值成功返回指向内存块的指针失败返回 NULL同上同上可能返回新地址是否可能移动内存否否是如果原内存块无法扩展适用场景快速分配一块未初始化内存分配数组并初始化扩展或缩小已分配内存内存对齐是适合任意类型是是注意必须用free释放内存