C-指针

1、何为指针?

指针:指针是保存变量地址的变量

指针类型,指针类型可由int 、double变量派生而来,比如指向int的指针类型,指向double的指针类型。

因此可将指针类型和int类型、double类型看作同一种东西,因此可以引申出指针类型的变量、指针类型的值。

而指针类型、指针类型的变量和指针类型的值我们都统称为“指针”

(%p可打印地址)

 

void* (空类型指针):可以指向任何类型的指针类型

但是当只告诉编译器内存地址,却不告诉编译器该地址上保存的数据类型,是无法从地址中取出值来的。

int a=5;
void *p;

p=&a;
printf("%d\n",*p);%错误,无法打印出来
printf(%d\n",*(int*)p);%必须告诉编译器所指向的数据类型

1.1指针运算

在C语言中对指针加1,地址会增加当前指针所指向的数据类型的长度

 

int main()
{
    int a[3] = {1,2,3};

    printf("%d\n",*a);
    printf("%d\n",*a+1);
    printf("0x%x\n",a);
    printf("0x%x\n",&a);
    printf("0x%x\n",a+1);
    printf("0x%x\n",&a+1);//虽然a与&a的结果相同,但是对它们进行+1操作,前者偏移量为一个int大小,后者偏移量为一个数组大小
}

标准输出:

1
2
0xd61afe14
0xd61afe14
0xd61afe18
0xd61afe20

 

在32、64位系统中:

  • char:1字节
  • int:4字节
  • float:4字节
  • double:8字节

指针:32位4字节,64位8字节

1.1.1空指针

空指针:没有指向任何一个对象的指针

1.1.2函数传参

在C中,函数传参都是值传递,传的都是参数的副本

当试图向函数传递数组时,其实传递的是指向数组初始数据的指针

2、内存

2.1虚拟地址

如今的操作系统会给应用程序的每一个进程分配独立的“虚拟地址空间”

操作系统将物理内存分配给虚拟地址空间,如果是禁止写入的区域,可能是被共享了。

如果内存不足时,操作系统会将程序中没有被引用的区域存入硬盘中,等到需要的时候再取回到内存中。

2.2 C的内存使用

2.2.1 C的变量的种类

根据作用域不同:

  • 全局变量:当程序被分割为多个源代码文件时,全局变量在各个源代码文件中都可以使用
  • 静态变量:加了static的全局变量,只能在当前文件中被使用,其他源代码文件无法使用
  • 局部变量:只能在语句块内使用

根据生命周期不同:

  • 静态变量:从程序运行时开始,到程序结束时结束
  • 局部变量:语句块结束时即被释放
  • malloc动态分配内存:遇到free()时释放

2.2.2只读存储区

大多数操作系统会将字符串常量和函数分配在只读内存区域,这样做的好处是当一个程序启动多次的时候,

通过在物理地址上共享数据可以节约物理内存。

2.2.3静态变量

从程序运行开始到程序运行结束持续存在的变量

2.3局部变量

局部变量通常保存在栈中,并且局部变量会重复使用内存区域

func(1,2)函数调用时,栈的使用情况:
func()中的本地变量
返回地址
1
2

一旦破坏了局部变量的内存区域,比如数组越界写入,就会造成该函数无法返回

2.4 递归调用:函数对自身的调用

遍历树的结构,图形中的各元素会用到递归,还有快速排序也会用到递归调用

2.5 malloc动态分配内存

malloc()根据传入参数的大小来分配内存块,返回指向内存块初始位置的指针

应用范例:

  • 动态分配结构体
  • 可变长数组

将字符数组换成字符指针,使用时分配内存

char *a;

malloc(sizeof(char)*len);

malloc不是系统调用,是标准库函数

 2.5.1 free()之后,对应的内存区域不会立刻返回给操作系统

直到在某个地方调用malloc(),该内存区域又会被重新分配

2.5.2 碎片化

碎片化指内存被分割成一个无法被使用的小碎块,只要使用malloc()就无法根本回避内存碎片化的问题。

2.6 动态内存分配函数

calloc():和malloc()相同的内存分配方式,会将内存区域清零后返回

realloc():  可以修改通过malloc()分配的内存的尺寸,如果通过指针传递的区域后面有足够的空间,则直接实施内存的扩容;如果内存不足,则另找一个

新空间,将数据复制过去。

使用realloc()的缺点:一是会影响程序的运行效率,另外不断对内存进行分配、释放的操作会造成内存的碎片化。

(使用realloc()时,如果传入的指针为NULL,则功能与malloc()相同,如果size为0,则功能与free()的功能相同)

如果malloc()分配内存失败,会返回NULL

2.7 内存溢出(忘记调用free())

 可以使用一些工具进行监测:比如Linux下的Valgrind ,Window下的CRT库

也可以对malloc与free的个数进行记录,在程序结束的时候看两者次数是否相同

2.8 内存对齐

  • 按照声明的顺序进行内存分配
  • 每个变量相对于起始位置的地址偏移必须是该变量类型大小的整数倍
  • 整个结构体的大小必须是变量类型最大值的整数倍

2.9 大小端

大端:高字节存放在低地址,低字节存放在高地址

小端:高字节存在高地址,低字节存放在低地址

辨别方法:联合体的方法或者强制类型转换方法

3 Const修饰符

const将类型修饰为:只读,const不一定代表的是常量

const一般是用来修饰函数的参数

常量指针 const char *  str:指向常量的指针,即str指向的对象不能修改

指针常量 char * const st:常量类型的指针,指针无法修改

(const修饰的是紧跟在它后面的单词)

3.1 typedef

typedef用于给类型定义别名

  static 修饰函数:只可在本文件中调用,程序的其他文件不可调用

如果其他文件想要调用,可以重写一个函数来调用这个静态函数

posted @ 2024-01-30 21:55  该说不唠  阅读(40)  评论(0)    收藏  举报