经过很长时间的学习C语言初阶,我都有一些迫不及待的为大家展示展示我的C语言初阶笔记了,这个博客主要讲C语言初阶中复杂概念,特定功能,栈区内存销毁导致的错误等内容。
1.复杂概念
1.1大小端
大端存储是指数据的高位放在低地址存储,数据的低位放在高地址存储。
小端存储是指数据的低位放在低地址存储,数据的高位放在高地址存储。
例如代码
#include<stdio.h> int main() { int a = 0x11223344; return 0; }
我们进入调试查看内存(对于如何查看内存可以点我查看如何查看内存)
我们画成图形来看
所以可以看到低位在低地址存储所以是小端存储。
对于如何查看电脑是大端存储还是小端存储我们可以写一个代码,代码如下
#include<stdio.h> int main() { int a = 1; char* p = (char*)&a; if (*p == 1) printf("小端存储\n"); else printf("大端存储\n"); return 0; }
我们运行可以看到
和我们预想的一样。但是这样的代码为什么可以判断呢?我们知道int类型占用4个字节,char占1个字节,也就是说我们利用一个char*的指针指向int的数据一次修改一个字节,我们可以画图为
如果这不理解可以查看博客点我查看指针基础
1.2 整形提升
C的整型算术运算总是至少以缺省整型类型的精度来进行的。
为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升。
1.2.1整形提升的意义:
表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度一般就是int的字节长度,同时也是CPU的通用寄存器的长度。
因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长
度。
通用CPU(general-purpose CPU)是难以直接实现两个8比特字节直接相加运算(虽然机器指令中可能有这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整型值,都必须先转换为int或unsigned int,然后才能送入CPU去执行运算。
1.2.2整形提升规则
整形提升是按照变量的数据类型的符号位来提升的。
1.2.3实例
例如我们有一个char类型的变量char a=-1,我们知道char占用1个字节也就是8个比特位故它的二进制序列为111111111.由于我们想进行整形提升得到int类型的a,我们知道整形提升是按照变量的数据类型的符号位来提升的,而符号位是1,int类型占4个字节,32个比特位,故它的二进制序列为111111111111111111111111111111111111。对于正数char类型的变量char b=1,它的二进制序列为00000001,我们对它进行整型提升,它的符号位是0,故我们得到int类型的b的二进制序列为00000000000000000000000000000001。对于unsigned char类型的变量unsigned char c=1,我们对它进行整形提升,和变量一样它的符号位是0(因为它没有符号,也就是说都是正数),故我们得到int类型的c的二进制序列为00000000000000000000000000000001。
2.特定功能
对于特定的功能我们可以想到冒泡排序,我们写一段代码将无序数组变成有序,代码如下
#include<stdio.h> int main() { int arr[5] = { 8,2,5,6,7 }; int i, j,max; for (i = 0; i < 4; i++) { for (j = 0; j < 4 - i; j++) { if (arr[j] > arr[j + 1]) { max = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = max; } } } for (i = -0; i < 5; i++) printf("%d ", arr[i]); return 0; }
我们运行可以看到
我们知道冒泡排序的功能就是让无序的数据按照非降序或非升序的方式进行排序。
3.栈区内存销毁导致的错误
说到栈区内存销毁导致的错误我们有一个很容易错的地方就是我们构造一个函数进行数据交换和在函数中定义数组再返回这个数组的地址。
3.1数据交换
数据交换在主函数中基本不会出现错误,但是我们想写一个数据交换的函数我们可能会写成
#include <stdio.h> void swap(int a, int b) { int temp; temp = a; a = b; b = temp; } int main() { int a = 10; int b = 20; swap(a, b); printf("a = %d\nb = %d", a, b); return 0; }
当我们运行时候我们发现
它的值并没有发生改变,这是因为我们的形参是在栈区开辟的空间,也就是说我们的形参以及temp都是在栈区创建的,它改变的是栈区的数据,不是想要的数据,我们可以画图理解为
对于修改我们需要用到指针,代码如下
#include <stdio.h> void swap(int* a, int* b) { int temp; temp = *a; *a = *b; *b = temp; } int main() { int a = 10; int b = 20; swap(&a, &b); printf("a = %d\nb = %d", a, b); return 0; }
由于传递的是地址,我们可以理解为
3.2栈区定义数组
例如代码
int* madearr() { int arr[10] = { 0,1,2,3,4,5,6,7,8,9 }; int i, j, min; for (i = 0; i < 9; i++) { for (j = 0; j < 9 - i; j++) { if (arr[j] < arr[j + 1]) { min = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = min; } } } return arr; }
我们在栈区创建了一个数组我们想要返回数组,但是当函数运行结束后函数所占用的空间会释放,导致数组arr释放,虽然返回了arr但是由于内存的释放返回了一些没有定义的,导致出现错误,想要解决这个问题,只需要我们将数组arr改为全局变量即可。
今天我们的内容就结束了,希望大家可以学到很多东西,最后别忘了点赞,评论,收藏哟。