• C语言入门-指针


    终于到了精髓的地方了,这确实有点懵,总感觉这太麻烦了,而且写着也不爽,还是怀念py或者java,但也没办法,还是要继续学下去。

    一、运算符&

    1. scanf("%d" , &i); 里的&
    2. 获取变量的地址,它的操作数必须是变量
    3. 地址的大小是否与int相同取决于编译器
    #include <stdio.h>
    int main(void)
    {
    	int i = 0;
    	printf("0x%x
    ", &i);
    	// 0x62fe4c
    	return 0;
    }
    

    &不能取的地址

    &不能对没有地址的东西取地址

    1. &(a+b) ?
    2. &(a++) ?

    二、指针

    1. 如果能够将获得变量的地址传递个一个函数,能否通过这个地址在那个函数内访问这个变量?
    2. scanf("%d" , &i);
    3. scanf()的原型应该是怎样的?我们需要一个参数保存别的变量的地址,如何表达能够保存地址的变量

    指针

    就是保存地址的变量 , *p

    // p是一个指针,现在把i的地址交给了p
    int i;
    int* p = &i;
    
    // 下面两种形式一样,p是一个指针,而q是一个普通的int变量
    int* p , q;
    int *p , q;
    

    指针变量

    1. 变量的值是内存的地址
    2. 普通变量的值是实际的值
    3. 指针变量的值是具有实际值的变量的地址

    作为参数的指针

    1. void f(int *p);
    2. 在被调用的时候得到了某个变量的地址
    3. int i = 0;f(&i);
    4. 在函数里面可以通过这个指针访问到外面的的这个i
    #include <stdio.h>
    
    void f(int *p);
    
    int main(void)
    {
    	int i = 6;
    	printf("&i=%p
    ", &i);
    	f(&i);
    
    	return 0;
    }
    
    void f(int *p)
    {
    	printf(" p=%p
    ", p);
    }
    
    // 可以看到这里获取的地址是相同的
    // &i=000000000062FE4C
    //  p=000000000062FE4C
    

    访问那个地址上的变量*

    1. *是一个单目运算符,用来访问指针的值所表示的地址上的变量
    2. 可以是右值也可以是左值
    3. int k = *p;
    4. *p = k + 1;

    *左值之所以叫左值

    1. 是因为出现在赋值号左边的不是变量,而是值,是表达式计算的结果
    2. a[0] = 2;
    3. *p = 3;
    4. 是特殊的值,所以叫左值
    #include <stdio.h>
    
    // 声明两个函数
    void f(int *p);
    void g(int k);
    
    
    int main(void)
    {
    	int i = 6;
    	printf("&i=%p
    ", &i);
    	f(&i);
    	
    	// 此时i的值已经发生了变化
    	g(i);
    
    	return 0;
    }
    
    
    // 传入的是地址
    void f(int *p)
    {
    	printf(" p=%p
    ", p);
    	printf("*p=%d
    ", *p);
    	*p = 66;
    }
    
    // 传入的普通int
    void g(int k){
    	printf("k=%d
    ", k);
    }
    
    
    // &i=000000000062FE4C
    //  p=000000000062FE4C
    // *p=6
    // k=66
    

    三、指针的使用

    指针应用场景一

    交换两个变量的值

    #include <stdio.h>
    
    void swap(int *pa , int *pb);
    
    int main()
    {
    	int a = 5;
    	int b = 10;
    	swap(&a , &b);
    	printf("a=%d , b=%d
    ", a , b);
    	return 0;
    }
    
    void swap(int *pa , int *pb){
        int t = *pa;
        *pa = *pb;
        *pb = t;
    }
    

    指针应用场景二

    1. 函数返回多个值,某些值就只能通过指针返回
    2. 传入的参数实际上是需要保存带回的结果的变量
    #include <stdio.h>
    
    void minmax(int a[] , int len , int *min , int *max);
    
    int main(void)
    {
    	int a[] = {1,2,3,4,5,6,7,8,9,12,13,14,15,34,35,66,};
    	int min , max;
    	minmax(a , sizeof(a)/sizeof(a[0]) , &min , &max);
    	printf("min = %d , max = %d 
    ", min , max);
    
    	return 0;
    }
    
    
    void minmax(int a[] , int len , int *min , int *max)
    {
    	int i;
    	*min = *max = a[0];
    	for (i = 0; i < len; i++)
    	{
    		if (a[i] > *max)
    		{
    			*max = a[i];
    		}
    		if (a[i] < *min)
    		{
    			*min = a[i];
    		}
    	}
    }
    

    指针应用场景二b

    1. 函数返回运算的状态,结果通过指针返回
    2. 常用的套路是让函数返回特殊的不属于有效范围内的值表示出错
      • -1 或 0
    3. 但是当任何数值都是有效的可能结果时,就得分开返回了
    #include <stdio.h>
    
    // 如果成功就返回1,否则就是0
    int divide(int a , int b , int *result);
    
    int main(void)
    {
    	int a = 5;
    	int b = 2;
    	int c;
    	if (divide(a,b,&c))
    	{
    		printf("%d/%d = %d
    ", a, b, c);
    	}
    
    	return 0;
    }
    
    int divide(int a , int b , int *result)
    {
    	int ret = 1;
    	if ( b== 0)
    	{
    		ret = 0;
    	}else{
    		*result = a/b;
    	}
    
    	return ret;
    }
    

    指针常见的错误

    定义了指针变量,还没有指向任何变量,就开始使用了

    四、指针与数组

    传入函数的数组成什么了?

    1. 函数参数表中的数组实际上是指针
    2. sizeof(a) == sizeof(int*)
    3. 但是可以用数组的运算符[]进行运算

    下面的四种函数原型是等价的

    int sum(int *ar , int n);
    int sum(int* , int);
    int sum(int ar[] , int n);
    int sum(int[] , int);
    

    数组变量是特殊的指针

    数组变量本身表达地址,所以

    1. int a[10]; int*p = a; // 无需用&取地址
    2. 但是数组的单元表达的是变量,需要用&取地址
    3. a == &a[0]

    []运算符可以对数组做,也可以对指针做

    1. p[0] <===> a[0]

    *运算符可以对指针做,也可以对数组做

    1. *a = 25

    数组变量是const的指针,所以不能被赋值

    五、指针与const

    1. 表示一旦得到了某个变量的地址,不能再指向其他变量
    	// q内写的地址不能被改变
    	int *const q = &i;
    	*q = 26; // ok
    	q++;   // error
    
    1. 表示不能通过这个指针去修改那个变量(并不能使得那个变量成为const)
    	const int *p = &i;
    	*p = 26; // error  (*p是不能变的)
    	i = 26; // ok
    	p = &j; // ok
    
    1. 看懂下面的意思?
    	const int * p1 = &i;
    	int const * p2 = &i;
    
    	int *const  p3 = &i;
    

    判断那个被const的标志是const在*的前面还是后面
    前面两个*p不能被修改,就像第二种情况

    const数组

    const int a[] = {1,2,3,4,5,6,};
    
    1. 数组变量已经是const的指针了,这里的const表明数组的每个单元都是const int
    2. 所以必须通过初始化进行赋值

    保护数组值

    1. 因为把数组传入函数时传递的是地址,所以那个函数内部可以修改数组的值
    2. 为了保护数组不被函数破坏,可以设置参数为const
    int sum(const int a[] , int length);
    

    六、指针运算

    1+1 = 2 ?那么指针加1等于什么呢

    #include <stdio.h>
    
    int main(void)
    {
    	char ac[] = {0,1,2,3,4,5,6,7,8,9,};
    	char *p = ac;
    
    	printf("p =%p
    ", p);
    	printf("p + 1 =%p
    
    ", p+1);
    	// p =000000000062FE30
    	// p + 1 =000000000062FE31
    	// 相差了1
        
    	int ai[] = {0,1,2,3,4,5,6,7,8,9,};
    	int *q = ai;
    
    	printf("q =%p
    ", q);
    	printf("q + 1 =%p
    
    ", q+1);
    	//q =000000000062FE00
    	//q + 1 =000000000062FE04
        // 相差了4
    
    	return 0;
    }
    

    我们不难发现,在char中,相差了1,在int中相差了4,这是怎么回事?

    sizeof(char) = 1 , sizeof(int) = 4

    对指针做一个加1的动作,意味着要把它移动到下一个单元去

    int a[10];
    int *p = a;
    *(p+1) --> a[1]
    

    如果指针不是指向一片连续分配的空间,如数组,那么这运算没有意义

    可以对指针的算术运算

    1. 给指针加、减、一个整数(+、+=、-、-=)
    2. 递增递减(++/--)
    3. 两个指针相减
    #include <stdio.h>
    
    int main(void)
    {
    	char ac[] = {0,1,2,3,4,20,6,7,8,9,};
    	char *p1 = &ac[5];
    	char *p2 = &ac[4];
    
    	printf("p1 - p2 = %d
    " , p1 - p2);
    	printf("*p1 - *p2 = %d
    " , *p1 - *p2);
    	// p1 - p2 = 1   也就是 5 -4
        // *p1 - *p2 = 16  也就是 20 - 4
    
        
    	int ai[] = {0,1,2,3,4,5,6,7,8,18,};
    	int *q1 = &ai[9];
    	int *q2 = &ai[2];
    
    	printf("q2 - q1 = %d
    ", q2 - q1);
    	printf("*q2 - *q1 = %d
    ", *q2 - *q1);
    	// q2 - q1 = -7      也就是 2 - 9
    	// *q2 - *q1 = -16   也就是 2 - 18
    
    	return 0;
    }
    

    *p++

    1. 取出p所指的那个数据来,完事之后顺便把p移动到下一个位置
    2. *的优先级虽然高,但没有++高
    3. 常用于数组类的连续空间操作
    4. 在某些cpu上,这可以直接被翻译成一条汇编指令
    	char ac[] = {0,1,2,3,4,20,6,7,8,9,-1};
    	char *p = &ac[0];
    
    	// 普通方法遍历数组
    	// int i ;
    	// for (i = 0; i < sizeof(ac)/sizeof(ac[0]); i++)
    	// {
    	// 	printf("%d
    ", ac[i]);
    	// }
    	
    	// 普通指针遍历数组
    	// for (p = ac; *p != -1; p++)
    	// {
    	// 	printf("%d
    ", *p);
    	// }
    
    	// printf("---------------
    ");
    	
    	// 使用*p++,快
    	while(*p != -1){
    		printf("%d
    ", *p++);
    	}
    

    0地址

    1. 当然你的内存中有0地址,但是0地址通常是个不能随便碰的地址
    2. 所以你的指针不应该具有0值
    3. 因此可以用0地址来表示特殊的事情
      • 返回的指针是无效的
      • 指针没有被真正的初始化(先初始化为0)
    4. NULL是一个预定定义的符号,表示0地址

    指针的类型

    1. 无论指向什么类型,所有的指针的大小都是一样的,因为都是地址
    2. 但是指向不同类型的指针是不能直接互相赋值的
    3. 只是为了避免用错指针

    用指针来做什么

    1. 需要传入较大的数据时用作参数
    2. 传入数组后对数组做操作
    3. 函数返回不止一个结果
    4. 需要用函数修改不止一个变量
    5. 动态申请内存时

    七、动态内存分配

    输入数据

    1. 如果输入数据时,先告诉你个数,然后再输入,要记录每个数据
    2. C99可以用变量做数组定义的大小,C99之前呢?
    #include <stdio.h>
    #include <stdlib.h>
    
    int main(void)
    {
    	int number;
    	int *a;
    	int i;
    	printf("请输入数量:
    ");
    	scanf("%d" , &number);
    
    	// 动态内存分配用malloc,此时a就相当于数组
    	a = (int*)malloc(number*sizeof(int));
    
    	for (i = 0; i < number; i++)
    	{
    		scanf("%d" , &a[i]);
    	}
    
    	for (i = number - 1; i >=0; i--)
    	{
    		printf("%d
    ", a[i]);
    	}
    
    	// 最后释放空间
    	free(a);
    
    	return 0;
    }
    

    malloc函数

    #include <stdlib.h>
    void* malloc(size_t size);
    
    1. 向malloc申请的空间的大小是以字节为单位的,需要什么类型就在sizeof里面写什么类型
    2. 返回的结果是void* , 需要类型转换为自己需要的类型
    3. 最后释放空间
    (int*)malloc(n*sizeof(int));
    

    没空间了?

    1. 如果申请失败则返回0 , 或者叫做NULL
    2. 你的系统可以给你多大的空间?
    #include <stdio.h>
    #include <stdlib.h>
    
    int main(void)
    {
    	void *p;
    	int cnt = 0;
    
    	while((p = malloc(100 * 1024 *1024))){
    		cnt ++ ;
    	}
    
    	printf("分配了%d00MB的空间
    ", cnt);
    
    	return 0;
    }
    // 分配了27200MB的空间
    

    free()

    1. 把申请得来的空间还给系统
    2. 只能还申请的空间的首地址
    3. 一个malloc对应一个free
  • 相关阅读:
    两个有序数组,找第k小的数//未完
    top详解--查看cpu及内存使用情况
    查看IO情况
    hadoop常用的调优参数
    zookeeper 的 javaAPI
    MapReduce优化
    mySQL索引数据数据结构 B+ 树
    P2670 [NOIP2015 普及组] 扫雷游戏
    P1887 乘积最大3
    1299. 五指山
  • 原文地址:https://www.cnblogs.com/mengd/p/11617982.html
Copyright © 2020-2023  润新知