C语言【指针】

C语言【指针】

1、指针类型长度随操作系统,64位操作系统为8Byte。

​ 具体说一下:地址相当于门牌号,一般一个地址指向的空间是一个字节(下面按一个字节叙述,计算机底层也许也有按双字节编码的)。对于64位机器,某一个地址有64位,所以在空间里存那某一个地址时,这64位即8Byte。这64位总共能表示的数量(即寻址数量)为2^64,十六进制0x0000000000000000到0xffffffffffffffff,这个数很大很大,以现在的内存空间来看,如果一个地址指向的空间是1Byte,根本用不完。

2、 %p为输出指针的占位符;&为取址运算符;*为取值运算符(即取地址对应的数据)。
3、指针一些基础知识,以代码为例。

定义指针时前面的类型的意义为说明指针每次移动的字节数。

int a = 100;
/** 	
*	下面的代码是声明并初始化。
*	之后想要改变指针指向,要修改p而不是*p(这里声明并初始化时用*p是指明定义的是一个指针)。
*	之后想要改变变量a的值,修改*p即可。
*/
int* p = &a;	
*p = 200;	// 修改*p时,a也会跟着改变
printf("%p\n", &a);	//	输出a的地址
printf("%p\n", p);	//	输出a的地址
printf("%d\n", a);	//	输出a的值
printf("%d\n", *p);	//	输出a的值
printf("%p\n", &p);	//	输出p的地址。p本身也有地址指向它,它也是个空间,空间里存的地址。

​ 指针就是个变量,存的别的数据地址,如果存自己的地址,那没啥意义。

​ 指针就是个变量,它是p而不是* p。* p是那个数据,参考上面代码。

​ 数组名、函数名等就是地址本身(或者说是表示数组或函数首地址的常量),所以,可以通过数组名给指针的方式使指针指向数组,反之不行,数组名和函数名不可更改。

/**
*	如果把指针赋给普通整型变量,可以。但如果想像*指针一样给这个变量加 * ,就不行了。
*/
int a = 100;
int * p = &a;
long long la = p;
*la;	// 会报错。
/**
* 不同指针类型
*/
int a = 100;
int* p = &a;
double * p1 = p;	// p1和p空间里的字节数一样,但去a那找的宽度不一样。* p1 去a那要找8Byte.
/**
* 野指针问题
*/
int * p;	// 一个野指针
*p = 100;	// 如果p是在函数里定义的,直接修改随机指向里的值,不会报错,但可能使程序崩掉。

指针的运算,即地址的运算,只不过指针可以接收运算的结果,而地址不行

​ 1.指针(地址)只能和整数作加减运算(包括两地址相加减)。本质就是地址的运算,指针可以接收结果。
对于int类型的指针来说,p+=1即p往后(高位)移动了4个字节。其他类型同理。

​ 2.同类型指针相减,得到一个ptrdiff_t类型的数据。其占位符为 %td 。

int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int *p1 = &arr[0];	// int * p1 = arr;
int *p2 = &arr[3];	// int * p2 = arr+3;   
p1[2];	// 等同arr[2] 指定数组后,指针可以这样使用
p2[2];	// arr[5], 看第三行代码
printf("%td\n", p1 - p2);	// -3  两地址相差3,(自动除过int的字节数)

​ 3.指针(地址)也可以作比较运算。栈先进后出,所以在函数中,一般先初始化的放在高地址,指针指向的时候可比较指向变量的地址大小。

4、指针和数组名的区别与共用。
  • 区别

    指针是变量,数组名是常量。

    sizeof(数组名) 返回为整个数组的字节数;当指针指向数组后,sizeof(指针) 返回仍然是跟随系统的字节数。

  • 相同。数组除了不能随意修改或指向外,基本可以当指针使用;当指针指向数组后,指针可以使用数组的所有方式操作数组。举个例子:

    int arr[20] = {10, 20, 30, ...};
    printf("%d\n", (arr+1)[0]);	// 20。 这里arr+1并没有修改arr的值。这个(arr+1)[0]可替换为*(arr+1).
    
5、指针数组。int *p[10]; 指针数组是一个大数组,里面存放的是一个个的指针。

​ * p[1] 和*(p[1])是一样的。表示第二个数组元素中指针指向的数值。

int a = 10, b = 20;
int *p[2] = {&a, &b};
printf("%d %d\n", *p[0], *p[1]);	// 10 20
6、数组指针。int (*p)[10]; 数组指针是一个指针,里面存放了一个数组。[10]表示这个二维的p加一就跳10个长度。这里这个p是一个二维的指针。

数组指针每次移动为 类型字节数*长度。sizeof(数组指针名) 仍然是随系统位数。

int (*p)[3];	// p+1即跳3个元素
int arr[2][3] = {1, 2, 3, 4, 5, 6};
p = arr;		// 这里是二维的,就不加&了
printf("%d\n", (*(p+1))[0]);	// 4 跳三个加个*再取数组第一个元素
printf("%d\n", (*(arr+1))[0]);	// 4 跳三个加个*再取数组第一个元素  二维数组名+1就跳到了下一行,记得两次取值
7、字符指针
char * str = "Hello World!";
str = "Hello Tom";	// 直接指向了一个新的字符数组
8、函数的传址
/**
* 函数的声明
*/
void inc(int *n){...}		// 在函数体中使用时记得时操作 (*n)
// void inc(int n[]){...}	//也可以这样
// void inc(int []);		//上行代码的声明

/**
* 函数的调用
*/
int n = 10;
inc(&n);
9、指针函数(返回指针的函数)
char * str_long(char*, char*);	//声明。应用如返回字符串或数组(但有函数运行完毕后空间释放导致找不到导致野指针问题。如果就是想用局部,可以声明为static。返回这个静态的。)。
10、函数指针(指向函数的指针)
函数名就是函数的首地址,所以可以定义一个指针,指向该函数。如果拿普通指针指向函数,可能会由于类型或长度不同而出问题,所以我们使用函数指针。
// 比如,有函数如下:
int max(int, int);	//声明。函数体略。

void main(){
    // 定义函数指针
    int (*fp)(int, int);
    // 指向函数
    fp = max;
    // 使用指针调用函数
    (*fp)(10, 20);
}
补充   一个函数指针的用法

typedef void(Timer0_Callback)(void*);

这个比之前的可厉害了,之前那个是搞出来个指向函数的指针,这个可以直接定义一个函数指针的数据类型(可看作数据类型),然后回调时更方便了(把函数指针的数据类型都定义出来了,当然把它当参数调用就更方便了)。

/**
* 用上面两种方法实现函数指针。
*/

#include<stdio.h>
#include<string.h>

typedef int(*C_a)(int, int);	// C_a 就是函数指针的数据类型

int max(int x, int y){
        return x>y?x:y ;
}


int main(int argc, char const *argv[])
{
		
    	// 直接定义出一个指向函数的指针
        int (*fp) (int, int);
        fp = max;
        printf("%d \n", fp(1, 2));
        printf("===========================\n");
    	
    	// 用 C_a这个数据类型来做
        C_a f = max;
        printf("%d \n", f(3, 4));
        return 0;
}
11、回调函数(函数指针的应用)
允许传递的参数为一个函数指针,即可以在一个函数里操作另一个函数,似乎是 Java 的lambda表达式的底层。
void initArr(int(*)());		// 回调函数的声明
12、多级指针。
int var = 10;
int *ptr = &var;	// 指向var,即ptr存的var的地址
int **pptr = &ptr;	// 指向ptr,即pptr存的ptr的地址
int ***ppptr = &pptr;	// 指向pptr,即ppptr存的pptr的地址
13、空指针。给指针赋NULL的指针。
NULL其实就是0 ---->  (void *)0

0是系统保留空间。

提供NULL只是为让咱操作指针时先if判断一下是不是没指向的指针。直接改0空间报的错更大,甚至程序直接崩掉。
14、野指针
野指针的几种情况:1、局部未赋初值的指针;2、越界的指针;3、已释放空间的指针。

1、可以使用<stdlib.h>库下的rand()函数 返回一个随机整数。具体使用?

2、sizeof的操作数不能是函数名,会警告,不会报错。我用gcc跑了一下,返回1

3、const与 * 与static的结合使用?

4、指针的空间分配?

5、C语言中的可变参数?

补一个案例
int a = 256;
int* p = &a;  
// int* p = &a + 1;  // 这里+1的话也是加了4返回给p
long long p1 = p;	// 这里就是为了下面可以加真实的数,而不是加定义类型的倍数
p = p1+1;			// 真正的+1 而不是+4
*p = 10;
printf("%d\n", a);	// 2560   凑巧10倍了
printf("%d\n", *p);	// 10

// 这个程序就是往左移了一个字节再改数,回到之前的变量输出混乱的值。
再补一个
int arr[5] = {10, 20, 30, 40, 50};
int (*ptr1)[5] = &arr;
printf("%p\t%p\n", arr, &arr);  // 对数组名取址仍然是之前的地址
printf("%d\n", ptr1 == arr);	// 1   但警报,不报错
printf("%d\n", *ptr1 == arr);	// 1
printf("%d\n", ptr1+1 == arr+1); //0  arr+1是第二个元素的地址,ptr1+1 越界了
posted on 2023-11-22 18:47  持枢丶  阅读(165)  评论(0编辑  收藏  举报