zoukankan      html  css  js  c++  java
  • 你必须知道的495个C语言问题,学习体会四

    本文,我们来学习下指针,这是个梦魇啊。无数次折磨着C语言学习者,无数次的内存泄露,无数次的访问失败,无数次的越界溢出,
    这些错误造就的仅仅是一个 跟随者,真正的优秀者必须要正视语言的局限,同时在最大限度的发挥它的优势,而发挥C语言优势的正是指针

    造物者的错

    int i =9;
    int*p =&i;
    *p =9;

    以上简单的代码,很不幸的混乱了指针指针的解引用,但是 ,这是最正宗的C语言语法,我初学C指针的时候,经常把* 当作指针的代名词,所以很当然的把*出现的地方当作 指针,所以我分不清上面代码的差异,所以我可能会写出像

    int*p;
    *p = malloc(10);

    这样的代码,因此,请记住,指针变量在这里就是p,而不是*p,当需要操作指针指向的内存时,才需要使用*来作为间接操作符。
    这样的误导其实是C语言语法的眼球错误,因为,我们经常把以上代码写成这样:

    char*p =(char*)malloc(10);

    而这样写是完全正确的。

    字符串?指针?数组?

    char*p1 ="hello world";
    char*p2 ="hello world";
    char p3[]="hello world";
    char p4[]={'h','e','l','l','o',' ','w','o','r','l','d'};

    额,少有人 弄懂了 以上三者的差异:怪只怪老是有人告诉我们 数组和指针没多大区别,怪只怪C语言里字符串这个东西 太难弄清晰,怪只怪我们大多数时候都不知道字符串到底是怎么结束的,''是个什么东西,怪只怪,C语言里很多字符串操作api都是不安全的。这样一来,我们估计要一辈子和C字符串打仗了,标准的C字符串实在不是个东西啊。

    发了一顿牢骚,我们还是回到代码本身,

    printf("p1 %s p2
    ",p1 == p2 ?"equal":"not equal");
    printf("p1 %s p3
    ",p1 == p3 ?"equal":"not equal");

    输出什么呢?

    1. p1 equal p2
    2. p1 not equal p3

    这说明 p1 与p2 相等,p1与p3不等,这是为什么?
    我们知道,"hello world"作为字符串是一个字面值常量,其存储在常量区,只有一份实体,所以凡是指向其位置的指针都是固定的值。而p3指代的是一个局部的字符数组,p3作为数组名称,其表示的是一个数组的首地址,而p1或者p2实际上是指针变量,这个指针变量就是字符常量的地址,所以p1和p2的值是一样的,而p3与二者不同就不足为怪了。

    而p4就不值得一提了,它很明确的告诉你,这是一个数组,但是和p3比起来,它又少了 一个'',所以,通过sizeof计算,p3 为12,而p4仅仅是11,这是p3与p4的最大不同,但是最大的相同是,他们都是数组(首地址)。

    最后,来总结下,指针和数组的区别:

    数组是一个由(同一类型)连续元素组成的预先分配的内存块,指针是一个对任何位置的(特定类型的)数据元素的引用。

    数组自动分配空间,但是不能重分配或者改变大小,指针必须被赋值以指向分配的空间(可能使用malloc),但是可以随意重新赋值(即指向不同的对象)。同时除了表示一个内存块的基地址外,还有许多其他用途。比如,可以作为动态分配的数组的首地址,对多个相似变量的一般访问,函数传参,遍历数组等。

    话分两头,指针与数组所谓的等价,使用malloc分配的内存块的指针通常可以被当作一个数组,也可以使用[ ]来引用。但是 你懂的,当使用sizeof时,二者还是有差别的,不然,怎么说C语言让人烦呢,什么话都是 编剧说好的。

    以上,看完 如果 你更糊涂了,请出门左转 ,把数组和指针当 不同的东西吧。

    函数参数

    指针作为参数参数 ,本质上是在(模拟)引用传参,这在C++中发扬光大了,现在在C中,我们姑且这么认为,比如当我们需要一个数组作为函数参数时,传它的首地址,需要传一个结构体时,传它的地址,如此,都可以当作传的数组或者结构体的引用。因此,可以说,引入引用这个思想概念,我们可以很好的清楚函数参数的 指针命题:

    • 数组指针参数
      事实上 不需要数组指针这么个东西,我们直接将一维数组的首地址传给函数,就可以了
    #include<stdio.h>
    #include<stdlib.h>
    void printTest(int a[],int lenght)
    {
    for(int i =0; i< lenght ;++i)
    printf("%d ",a[i]);
    printf("
    ");
    }
    int main()
    {
    int a[2]={2,3};
    printTest(a,2);
    return0;
    }

    可以这么说,作为函数参数,a就是a[2]数组的 引用名,所以,操作的就是a[2]数组本身。换用常规的理解,一个数组的数组名是该数组的地址,所以在函数参数中,直接退化为指针了,虽然,我的函数参数就是a[] ,但是其本质应该是int *a。

    再看一段:

    二维数组的例子,希望你不要骂娘,或者觉得我有病

    #include<stdio.h>
    #include<stdlib.h>
    void printTest1(char(*a)[10],int lenght)
    {
    int i =0;
    for(;i < lenght;++i)
    printf("%s
    ",*(a+i));
    printf("
    ");
    }
    void printTest2(char*a[],int lenght)
    {
    int i =0;
    for(;i<lenght;i++)
    printf("%s
    ",*(a+i));
    printf("
    ");
    }
    int main(int argc,char* argv[])
    {
    char a1[2][10]={"hello","world"};
    char*a2[10]={"hello2","world2"};
    char(*p1)[10]= a1;
    char** p2 = a2;
    printTest1(p1,2);
    printTest2(p2,2);
    return0;
    }

    试试使用printTest2 打印p1或者相反,你就应该明白为何我这么多次一举了。
    代码中a1与a2的区别,如果你懂了,那本文就达到它的目的了。这正说明了数组与指针的天囊之别。

    未完待续..

  • 相关阅读:
    Use Module and Function instead of Class in Python
    以命令行方式使用Desktop版Ubuntu
    python中两种拷贝目录方法的比较
    查找重复文件并删除的工具
    Manage sshd Service on CentOS
    Java多线程间的数据共享
    并发 总结
    MapReduce 过程分析
    java能不能自己写一个类叫java.lang.System/String正确答案
    生产者消费者模式--阻塞队列--LOCK,Condition--线程池
  • 原文地址:https://www.cnblogs.com/Stultz-Lee/p/6777808.html
Copyright © 2011-2022 走看看