c初探:数据类型、数组、内存布局、指针
c初探:数据类型、数组、内存布局、指针
windows命令行 (可以不用搭理这里,在Linux玩就行)
Windows C/C++编译器: https://sourceforge.net/projects/mingw/files/
配置环境变量 PATH: ${MinGW安装目录}/MinGW/bin
c与c++
C语言是一门通用计算机编程语言,广泛应用于底层开发。
c语句是面向过程的语言,c++ 是面向对象的语言,C++对c进行扩展。
c是c++ 的子集,c++ 是c的超集,所以大部c语言程序都可以不加修改的拿到c++下使用。
1、基本数据类型
1.**signed**----有符号,可修饰char、int。Int是默认有符号的。 2.**unsigned**-----无符号,修饰int 、char
整型 | 字节 | 取值范围 | 占位 |
---|---|---|---|
int | 4 | -2,147,483,648 到 2,147,483,647 | %d |
unsigned int | 4 | 0 到 4,294,967,295 | %u |
short | 2 | -32,768 到 32,767 | %hd |
unsigned short | 2 | 0 到 65,535 | %hu |
long | 4 | -2,147,483,648 到 2,147,483,647 | %ld |
unsigned long | 4 | 0 到 4,294,967,295 | %lu |
char | 1 | -128 到 127 | %c |
unsigned char | 1 | 0 到 255 | %c |
为了得到某个类型或某个变量在特定平台上的准确大小,使用 sizeof 运算符。
表达式 sizeof(type) 得到对象或类型的存储字节大小。
long int 其实就是长整型 = long 可以省去int
在标准中,规定 int至少和short一样长,long至少和int一样长。
为什么会存在long?
long和int在早期16位电脑时候 int 2字节,long 4字节,而计算机发展到现在,一般32、64下,long和int一样。和java类比的话,java的long就是 long long 8字节
格式化还有:
8进制 %o
16进制 小写: %x 大写:%X
(0x)+16进制前面 %#x
浮点型 | 字节 | 精度 | 占位 |
---|---|---|---|
float | 4 | 6位小数 | %f |
double | 8 | 15位小数 | %lf |
long double | 8 | 19位小数 | %Lf |
C99标准以前,C语言里面是没有bool,C++ 里面才有,
C99标准里面定义了bool类型,需要引入头文件stdbool.h
bool类型有只有两个值:true =1 、false=0。
因此实际上bool就是一个int
所以在c/c++中 if 遵循一个规则, 非0为true,非空为true;
NULL 其实也就是被define为了 0
2、格式化
include <stdio.h>
printf、sprintf等
sprintf:
将格式化的数据写入第一个参数
char str[100]; sprintf(str, "img/png_%d.png", 1); printf("%s", str); //使用 0 补到3个字符 sprintf(str, "img/png_%03d.png", 1); printf("%s", str);
关于include的“<>”和“""”的却别
-
"" 指的是相对路径
-
<> 查找系统的,以及我们给它定义的目录
输出控制符
常用的输出控制符主要有以下几个:
控制符 | 说明 | 示例 |
---|---|---|
%d | 按十进制整型数据的实际长度输出 | printf("%d",123);输出123 |
%ld | 输出长整型数据。 | |
%md | m 为指定的输出字段的宽度。如果数据的位数小于 m,则左端补以空格,若大于 m,则按实际位数输出。 | |
%u | 输出无符号整型(unsigned)。输出无符号整型时也可以用 %d,这时是将无符号转换成有符号数,然后输出。但编程的时候最好不要这么写,因为这样要进行一次转换,使 CPU 多做一次无用功。 | printf("%u",123);输出123 |
%c | 字符型。可以把输入的数字按照ASCII码相应转换为对应的字符 | printf("%c\n",64)输出A |
%p | 以16进制形式输出指针 | printf("%010p","lvlv");输出:0x004007e6 |
%f | 用来输出实数,包括单精度和双精度,以小数形式输出。不指定字段宽度,由系统自动指定,整数部分全部输出,小数部分输出 6 位,超过 6 位的四舍五入。 | printf("%.9f %.9lf",0.000000123,0.000000123);输出0.000000123 0.000000123。注意指定精度,否则printf默认精确到小数点后六位 |
%.mf | 输出实数时小数点后保留 m 位,注意 m 前面有个点。 | |
%o | 以八进制整数形式输出,这个就用得很少了,了解一下就行了。 | printf("0%o",123);输出0173 |
%s | 用来输出字符串。用 %s 输出字符串同前面直接输出字符串是一样的。但是此时要先定义字符数组或字符指针存储或指向字符串,这个稍后再讲。 | printf("%s","测试test");输出:测试test |
%x(或 %X 或 %#x 或 %#X) | 以十六进制形式输出整数,这个很重要。 | printf("0x%x 0x%X",123,123);输出0x7b 0x7B |
%e(%E) | 科学计数法,使用指数(Exponent)表示浮点数,此处”e”的大小写代表在输出时“e”的大小写 | printf("%e %E",0.000000123,0.000000123);输出1.230000e-07 1.230000E-07 |
%x、%X、%#x、%#X 的区别
一定要掌握 %x(或 %X 或 %#x 或 %#X),因为调试的时候经常要将内存中的二进制代码全部输出,然后用十六进制显示出来。下面写一个程序看看它们四个有什么区别:
# include <stdio.h> int main(void) { int i = 47; printf("%x\n", i); printf("%X\n", i); printf("%#x\n", i); printf("%#X\n", i); return 0; }
在 VC++ 6.0 中的输出结果: 2f 2F 0x2f 0X2F
从输出结果可以看出:如果是小写的x
,输出的字母就是小写的;如果是大写的X
,输出的字母就是大写的;如果加一个#
,就以标准的十六进制形式输出。
最好是加一个#
,否则如果输出的十六进制数正好没有字母的话会误认为是一个十进制数呢!总之,不加#容易造成误解。但是如果输出0x2f
或 0x2F
,那么人家一看就知道是十六进制。而且%#x
和%#X
中,笔者觉得大写的比较好,因为大写是绝对标准的十六进制写法。
3、数组与内存布局
数组 : 连续的内存
//java int[] a //c //必须声明时候确定大小 int a[10] //或者 直接初始化 int a[] = {1,2,3} //大小 printf("%d",sizeof(a)/sizeof(int));
栈内存限制 linux:ulimit -a 查看
但是直接分配这么大不行,因为堆栈可能保存参数,返回地址等等信息
动态内存申请
malloc
没有初始化内存的内容,一般调用函数memset
来初始化这部分的内存空间.
使用
malloc
申请了内存后,有可能这块内存是复用之前释放掉的,里面还可能会有数据,所以,使用memset
来清空数据之前的数据,并初始化自己的数据
int k = 1*1024*1024; //在堆中动态申请内存 int *j = (int *)malloc(k); //将j指向的内存初始化为0,长度为k memset(j,0,k); //内存释放 free(j); j = NULL;//j = 0;
calloc
申请内存并将初始化内存数据为NULL.
int *pn = (int*)calloc(10, sizeof(int));
//申请内存并将内存初始化为null即为0 int *p = (int*)calloc(10, sizeof(int));//申请40个字节的长度 //上面一句话相当于下面两句话 int *p = (int*)malloc(sizeof(int)*10); memset(p,0, sizeof(int)*10); //内存释放 free(p); p = NULL;
realloc
对malloc申请的内存进行大小的调整.
char *a = (char*)malloc(10); realloc(a,20);//对a进行扩容到20
特别的:
alloca
在栈申请内存,因此无需释放.
int *p = (int *)alloca(sizeof(int) * 10);
物理内存
物理内存指通过物理内存条而获得的内存空间
虚拟内存
一种内存管理技术
电脑中所运行的程序均需经由内存执行,若执行的程序占用内存很大,则会导致内存消耗殆尽。
虚拟内存技术还会匀出一部分硬盘空间来充当内存使用。
代码段:
存放程序执行代码(cpu要执行的指令)
栈是向低地址扩展数据结构
堆是向高地址扩展数据结构
进程分配内存主要由两个系统调用完成:brk和mmap 。
- brk是将_edata(指带堆位置的指针)往高地址推;
- mmap 找一块空闲的虚拟内存。
通过glibc (C标准库)中提供的malloc函数完成内存申请
malloc小于128k的内存,使用brk分配内存,将_edata往高地址推,大于128k则使用mmap
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
· PowerShell开发游戏 · 打蜜蜂
· 凌晨三点救火实录:Java内存泄漏的七个神坑,你至少踩过三个!