c/c++语言开发共享c语言数组与指针详解(上)

彻底搞懂c语言数组与指针 部分引用 1. “c语言指针怎么理解 知乎” 2. “程序设计入门————c语言 (浙江大学翁恺)” 3. 《c primer plus》第六版 基础知识 1. 指针基础 &:代表对变量取地址 int 或char 或者把这个星号紧贴着变量比如int a = &b: …


彻底搞懂c语言数组与指针

部分引用

  1. 程序设计入门————c语言 (浙江大学翁恺)
  2. 《c primer plus》第六版

基础知识

1. 指针基础

c语言数组与指针详解(上)

  • &:代表对变量取地址
  • int*或char*或者把这个星号紧贴着变量比如int *a = &b: 代表新建一个用来储存地址的变量,这也代表&b这个值的类型是int*。
  • int *a, b 或 int* a, b 中只有a是int指针类型,b是int整型。

  • 关于电脑大小端的讨论:大端是指是指数据的高字节,保存在内存的低地址中,而数据的低字节,保存在内存的高地址中。小端是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内在的低地址中。例如下图:

    c语言数组与指针详解(上)

  • 假设 int b=4; int *a = &b 则*a=4: 因为*a代表a变量中的地址所指的值。重复一下对比:&b是指针类型,值是地址;*b是实际指针b所指的变量的值。
  • 如果打印地址则用%p,以16进制显示指针的值,而不是用%x,如: printf("%pn", &i)
  • 32位和64位下指针的长处不同,32位下为4个字节,和int一样,64位下8个字节。

2. 数组基础

  • 数组特点:
    • 数组大小一旦定义不可改变
    • 所有的元素具有相同的数据类型
    • 数组中的元素在内存中是连续依次排列的
    • 数组的集成初始化:int a[] = {1,2,3,4,25,6,5,4}; 即让编译器自己来数元素的数量
    • 如果这样赋值:int a[3] = {2}; 则结果是这个数组有三个元素,a[0]=2,a[1]=0,a[2]=0;编译器自动补全后面的数字为0;
    • 集成初始化的定位:int a[6] = {[1] = 2, [3] = 3, 6}; 结果是 a[0]=0, a[1]=2, a[2]=0, a[3]=3, a[4]=6, a[5]=0; 适合初始数据稀疏的数组。
    • 如果想让定义的数组变成只读,即不可修改的类型,则可以在最前面加上一个const。如:const int a[2] = {2, 3, 4}; 当然此条也适用于二维数组。
    • 数组只有在最开始即定义初始化的时候可以集成赋值,下列赋值方法错误:int a[3] = {}; a[3] = {1,2,3}; 这时会错误,因为这是一个单个元素赋值的方法,况且a[3]已经超出了范围。
    • 求数组的大小,稳定的方法是 sizeof(a)/sizeof(a[0]) ; 就算修改初始数组a中的数据,也不用修改遍历时的代码;
    • 数组作为函数参数时,往往必须再用另一参数来传入数组的大小。
      • 不能在[]中给出数组的大小
      • 不能在函数中再利用sizeof计算数组的元素的个数
    • 定义数组a, b:int a[10]; b=[]; 则不能直接用b=a来给数组b赋值。
    • 对于数组a,&a=a=&a[0]
  • 二维数组:
    • int a[2][3] 相当于一个2行3列的矩阵
    • int a[0][0] 表示第一行第一列,意味着下标同样也是从0开始
    • 二维数组的遍历需要嵌套for循环
    • a[i][j]表示第i行第j列的元素,a[i,j]是一个表达式,相当于a[j],没有意义,会报错。
    • 二维数组初始化的时候列数可以省略,行数可以由编译器来数。例如:inta[][5] = {{0,1,2,3,4},{2,3,4,5,6}};
    • 初始化二维数组的两种方法:部分初始化则将剩下的那部分赋值为0
      • int a[2][3] = {{5, 6},{7, 8}}; 则a[0][0]=5, a[0][1]=6, a[0][2]=0, a[1][0]=7, a[1][1]=8, a[1][2]=0;
      • int a[2][3] = {5, 6, 7, 8}; 则a[0][0]=5, a[0][1]=6, a[0][2]=7, a[1][0]=8, a[1][1]=0, a[1][2]=0;
    • 三位数组理解方法:比如int box[10][20][30]; 则可以理解成由10个二维数组(每个是20行30列)堆叠起来,这20个数组元素中的每个元素是内含30个元素的数组。

通过程序加深理解一些概念

1. 数组的免费精选名字大全就相当于这个数组第一个元素的内存地址:

#include <stdio.h>   int main(){     int a[10]={1,2,3,4,5,6,7,8,9,10};  //定义一个整型数组,这里a实质上是一个指向数组中第一个数据a[0]的指针     int *p=a;        printf("%dn",*p);     printf("%d",*(p+1));              return 0; } 

返回结果为:
1
2

2. 利用指针对数组进行初始化

#include <stdio.h>  int main(){     int d[10];     int *e;          e=&d[0]; //e保存了数组d的第一个数据的地址          for (int i=0; i<10; i++){         *e = i; //把该地址中的数据依次赋值0,1,2,3,4,5,6,7,8,9          e++; //地址累加一次,也就是数组中下一个数据的地址     }           for (int i=0; i<10; i++){           printf("%dn", d[i]);  //打印数组d中的所有元素      }      return 0;      } 

3. 数组作为函数参数时,往往必须再用另一参数来传入数组的大小。

  • 不能在[]中给出数组的大小
  • 不能在函数中再利用sizeof计算数组的元素的个数
  • 声明数组形参时,下列方法等价:(函数原型可以省略参数名)切记:此为函数声明时用法,不可直接引用于函数定义
    • int sum(int *a, int len);
    • int sum(int *, int);
    • int sum(int ar[], int n);
    • int sum(int [], int);
  • 函数定义中不能省略参数名,以下两种方法可行且等价:
    • int sum(int *a, int len)
      {
      //省略其他代码
      }
    • int sum(int a[], int len)
      {
      //省略其他代码
      }
#include <stdio.h> //此方法为最简单,最基础的数组遍历  int search(int key, int a[], int len)  int main() {     int a[]= {1,3,5,2,9,4,12,23,15,32};     int r = search (12, a, sizeof(a)/sizeof(a[0]));   //传入参数的时候在main函数中计算好函数的个数传入到search函数中;另外,此处a传入的时a[0]元素的地址。     printf("%dn", r);     return 0;  }    int search(int key, int a[], int len)   //len变量必须要加,因为在search函数中无法用sizeof函数计算数组的大小 {     int ret = -1;     int i;     for (i=0; i<len; i++){         if (key == a[i]){             ret = i;             break;         }     }     return ret; }

4. 二维数组中数组名的含义

#include<stdio.h>  int main(){     int a[2][3]={{1,2,3},{4,5,6}};          printf("%pn",a);   //输出指针a数据,也就是指针a[0]的地址      printf("%pn",a+1);   //输出a+1的数据 ,也就是a[1]的地址     printf("%pn",&a[0]);     printf("%pn",&a[1]);     //验证上述     printf("%pn",(*a)+1);  //输出的是a[0][1]的地址     printf("%pn",&a[0][1]);   //验证     printf("%dn",*(a[0]));  //输出的是a[0]a[0]的值      printf("%dn",*(*(a+1)+1));  //输出的是a[1][1]的值  }

注意:a是一个2行3列的数值,a+1表示的a[1]值所在的地址,a[1]的值又代表a[1][0]的值所在的地址

5. int与char指针类型的区别

int i=2; int *a=&i 和 char j=’m’; char *b=&j 区别在于:int占据四个字节,a中虽然记载的i的第一个字节的地址,但是由于a是int类型的指针,*a读取的时候自动再往后读3个字节;而b是char类型的指针,则只读取当前记录的这一个字节,自然不能用指针b来保存int i的值。

#include <stdio.h>  int main(){     int i = 2;     int j = 's';          int *a = &i;     char *b = &i;          int *m = &j;     char *n = &j;          printf("%dn", *a);     printf("%dn", *b);  //会产生warning      /* 解释为什么前两行输出为什么一样:      * 在存储中,2作为int存储为 00000010 00000000 00000000 00000000 四个字节,用此种表示方法是因为我的电脑是个小端电脑(little-endian)。详述见下条。      * a 和 b 所记录的都是四个字节中第一个字节的地址,*a读取到的是4个完整的字节,而*b读取到的是第一个字节 00000010,由于巧合,二者所代表的都是数字1。       */      printf("%cn", *m);  //会产生warning     printf("%cn", *n);          return 0;  } 

6. 指针内存位置理解深入剖析(一定在自己的电脑上运行试下)

#include <stdio.h>  int main() {     int a = 1, b = 2;     char c = 'c', d = 'd';     int *m, *n;     char *j, *k;      m = &a;     n = &b;     j = &c;     k = &d;          printf("int变量在内存中的存储情况");     printf("a  %pn", m);     printf("b  %pn", n);      //由于栈自顶向下的存储方法,内存位置上a与b两个元素是紧邻着的,a位置高,b低,相差四个字节。      printf("n");     printf("对int指针变量+1会得到什么结果,实际改变几个字节?n");     printf("&b+1  %pn", n+1);     printf("&a-&b %dn", m-n);     //上两个语句测试得到结果,a与b的地址m,n相差并不是4而是1。因为相差的是存储单元数而不是字节数      printf("n");     printf("char指针变量什么情况,本来就相差1个字节?n");     printf("c  %pn", j);     printf("d  %pn", k);     //由于char变量只占1个字节,所以这两个变量位置地址相差为1      printf("n");     printf("n");          printf("int型指针变量占据几个字节?n");     printf("&m  %pn", &m);     printf("&n  %pn", &n);     printf("char型指针变量占据几个字节?n");     printf("&j  %pn", &j);     printf("&k  %pn", &k);      //可以得到,在64位系统上,像m,n这种指针本身的存储都是占据8个字节,不管是char类型还是int类型。           return 0;  } 

一句话概括指针的加减:指针加1,指针的值递增它所指向类型的大小(以字节位单位)
dates + 2 == &dates[2]
*(dates + 2) == dates[2]

本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。

ctvol管理联系方式QQ:251552304

本文章地址:https://www.ctvol.com/c-cdevelopment/600381.html

(0)
上一篇 2021年5月9日
下一篇 2021年5月9日

精彩推荐