服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C/C++ - C语言零基础讲解指针和数组

C语言零基础讲解指针和数组

2022-11-10 15:12清风自在 流水潺潺 C/C++

由于数据的表现形式多种多样,还有字符型和其它的数值类型,因此仅有基本数据类型是不够的。是否可以通过基本数据类型的组合抽象构造其它的数据类型呢?下面是小编为大家带来的C语言数组与指针详解的知识

一、指针数组分析-上

1.数组的本质

  • 数组是一段连续的内存空间
  • 数组的空间大小为 sizeof(array_type) * array_size
  • 数组名可看做指向数组第一个元素的常量指针

下面看一段代码:

#include <stdio.h>

int main()
{
  int a[5] = {0};
  int* p = NULL;
  
  printf("a = 0x%X\n", (unsigned int)(a));
  printf("a + 1 = 0x%X\n", (unsigned int)(a + 1));
  
  printf("p = 0x%X\n", (unsigned int)(p));
  printf("p + 1 = 0x%X\n", (unsigned int)(p + 1));
  
  return 0;
}

输出结果如下:

C语言零基础讲解指针和数组

通过这段代码说明指针运算是合法的。

2.指针的运算

指针是一种特殊的变量,与整数的运算规则为

p + n; <-->(unsigned int)p + n*sizeof(*p);

结论∶

当指针 p 指向一个同类型的数组的元素时:p+1 将指向当前元素的下一个元素;p-1 将指向当前元素的上一个元素。

  • 指针之间只支持减法运算
  • 参与减法运算的指针类型必须相同

p1- p2; <--> ((unsigned int)p1 - (unsigned int)p2) / sizeof(type);

注意:

  • 只有当两个指针指向同一个数组中的元素时,指针相减才有意义,其意义为指针所指元素的下标差
  • 当两个指针指向的元素不在同一个数组中时,结果未定义

下面看一段简单的指针运算代码:

#include <stdio.h>

int main()
{
  char s1[] = {'H', 'e', 'l', 'l', 'o'};
  int i = 0;
  char s2[] = {'W', 'o', 'r', 'l', 'd'};
  char* p0 = s1;
  char* p1 = &s1[3];
  char* p2 = s2;
  int* p = &i;
	
  printf("%d\n", p0 - p1);
  //printf("%d\n", p0 + p2);  //ERROR
  printf("%d\n", p0 - p2);
  //printf("%d\n", p0 - p);   //ERROR
  //printf("%d\n", p0 * p2);  //ERROR
  //printf("%d\n", p0 / p2);  //ERROR
	
  return 0;
}

输出结果如下:

C语言零基础讲解指针和数组

注意两个指针指向不同的数组,虽然它们两相减符合语法,但是最后的结果肯定没有意义。

再来看一段指针运算的应用代码:

#include <stdio.h>

#define DIM(a) (sizeof(a) / sizeof(*a))

int main()
{
  char s[] = {'H', 'e', 'l', 'l', 'o'};
  char* pBegin = s;
  char* pEnd = s + DIM(s); // Key point
  char* p = NULL;
  
  printf("pBegin = %p\n", pBegin);
  printf("pEnd = %p\n", pEnd);
  
  printf("Size: %d\n", pEnd - pBegin);
	
  for(p=pBegin; p<pEnd; p++)
  {
      printf("%c", *p);
  }
  
  printf("\n");
 
  return 0;
}

输出结果如下:

C语言零基础讲解指针和数组

注意以下几点:

  • 数组大小的计算方法:#define DIM(a) (sizeof(a) / sizeof(*a))
  • char* pEnd = s + DIM(s); // Key point ==> pEnd 指向 'o' 后面的地址 ==> 这在 C 语言中是一个擦边球的边界位置,也是一个技巧,在这个边界位置可以认为该指针是合法的,可以和其他指针进行比较运算和减法运算等,在 C++ 标准库里面也合法

3.指针的比较

  • 指针也可以进行关系运算 (<,<=,>,>=)
  • 指针关系运算的前提是同时指向同一个数组中的元素
  • 任意两个指针之间的比较运算(==,!=)无限制
  • 参与比较运算的指针类型必须相同

4.小结

  • 数组声明时编译器自动分配一片连续的内存空间
  • 指针声明时只分配了用于容纳地址值的 4 字节空间
  • 指针和整数可以进行运算,其结果为指针
  • 指针之间只支持减法运算,其结果为数组元素下标差
  • 指针之间支持比较运算,其类型必须相同

 

二、指针与数组分析-下

1.数组的访问方式

以下标的形式访问数组中的元素

C语言零基础讲解指针和数组

以指针的形式访问数组中的元素

C语言零基础讲解指针和数组

2.下标形式 VS 指针形式

  • 指针以固定增量在数组中移动时,效率高于下标形式
  • 指针增量为1且硬件具有硬件增量模型时,效率更高
  • 下标形式与指针形式的转换

a[n] <--> *(a +n) <--> *(n + a) <--> n[a]

注意:现代编译器的生成代码优化率已大大提高,在固定增量时,下标形式的效率已经和指针形式相当;但从可读性和代码维护的角度来看,下标形式更优。

下面看一个数组的访问方式代码:

#include <stdio.h>

int main()
{
  int a[5] = {0};
  int* p = a;
  int i = 0;
  
  for(i=0; i<5; i++)
  {
      p[i] = i + 1;
  }
  
  for(i=0; i<5; i++)
  {
      printf("a[%d] = %d\n", i, *(a + i));
  }
  
  printf("\n");
  
  for(i=0; i<5; i++)
  {
      i[a] = i + 10;
  }
  
  for(i=0; i<5; i++)
  {
      printf("p[%d] = %d\n", i, p[i]);
  }
  
  return 0;
}

输出结果如下:

C语言零基础讲解指针和数组

注意这个奇怪的写法:i[a] = i + 10; ==> a[i] = i + 10;

下面通过一个实例,说明数组和指针的不同:

ext.c:

int a[] = {1, 2, 3, 4, 5};

test.c:

#include <stdio.h>

int main()
{
  extern int a[];
  
  printf("&a = %p\n", &a);
  printf("a = %p\n", a);
  printf("*a = %d\n", *a);

  return 0;
}

输出结果如下:

C语言零基础讲解指针和数组

下面来验证一下数组名究竟是不是指针,将 test.c 改成:

#include <stdio.h>

int main()
{
  extern int* a;
  
  printf("&a = %p\n", &a);
  printf("a = %p\n", a);
  printf("*a = %d\n", *a);

  return 0;
}

输出结果如下:

C语言零基础讲解指针和数组

ext.c 中 a[ ] 的地址为 0x804a014,test.c 中的extern int* a; 只是申明标识符 a,编译器会认为在这之前就已经给了地址值,就是 0x804a014,所以printf("a = %p\n", a); 就是打印0x804a014 地址中的 4 个字节的数,也就是 a[ ] 数组中的第一个元素 1,所以打印 0x1,*a 就是取 0x1 地址中的数,但是这个地址值是留给操作系统的,不可访问,访问就会产生段错误。

3.a 和 &a 的区别

  • a 为数组首元素的地址
  • &a 为整个数组的地址
  • a 和 &a 的区别在于指针运算

C语言零基础讲解指针和数组

这个就能看出 a + 1 和 &a + 1 的不同,a + 1 增加的步长是一个元素的大小,&a + 1 则是增加的步长是整个数组的大小。

下面看一个指针运算的经典问题:

#include <stdio.h>

int main()
{
  int a[5] = {1, 2, 3, 4, 5};
  int* p1 = (int*)(&a + 1); 
  int* p2 = (int*)((int)a + 1);
  int* p3 = (int*)(a + 1);
  
  printf("%d, %d, %d\n", p1[-1], p2[0], p3[1]);
  
  return 0;
}

输出结果如下:

C语言零基础讲解指针和数组

p1[-1] 就是 *(p1 - 1),由于 p1 指向的元素是 5 后面的位置,减 1 之后就指向了 5;p2 的地址是 0x804a015(注意 linux 系统为小端系统),*p2 就是 0x02000000,对应十进制的值就是 33554432;p3 的地址为 &a[1],所以 p3[1] 就是 3 了。

C语言零基础讲解指针和数组

4.数组参数

数组作为函数参数时,编译器将其编译成对应的指针

C语言零基础讲解指针和数组

结论:一般情况下,当定义的函数中有数组参数时,需要定义另一个参数来标示数组的大小。

下面看一段代码:

#include <stdio.h>

void func1(char a[5])
{
  printf("In func1: sizeof(a) = %d\n", sizeof(a));
  
  *a = 'a';
  
  a = NULL;
}

void func2(char b[])
{
  printf("In func2: sizeof(b) = %d\n", sizeof(b));
  
  *b = 'b';
  
  b = NULL;
}

int main()
{
  char array[10] = {0};
  
  func1(array);
  
  printf("array[0] = %c\n", array[0]);
  
  func2(array);
  
  printf("array[0] = %c\n", array[0]);
  
  return 0;
}

输出结果如下:

C语言零基础讲解指针和数组

这段代码就说明数组参数退化成指针,因为 sizeof(a) 为 4 个字节,而不是 5 个字节。

5.小结

数组名和指针仅使用方式相同

  • 数组名的本质不是指针
  • 指针的本质不是数组

数组名并不是数组的地址,而是数组首元素的地址

函数的数组参数退化为指针

到此这篇关于C语言零基础讲解指针和数组的文章就介绍到这了,更多相关C语言 指针和数组内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!

原文链接:https://blog.csdn.net/weixin_43129713/article/details/123715688

延伸 · 阅读

精彩推荐
  • C/C++给C语言初学者的学习建议

    给C语言初学者的学习建议

    在本篇文章里小编给大家分享的是关于C语言学习建议的相关内容,有兴趣的朋友们可以学习参考下。...

    少儿编程9252021-09-13
  • C/C++老程序员教你一天时间完成C语言扫雷游戏

    老程序员教你一天时间完成C语言扫雷游戏

    这篇文章主要为大家详细介绍了C语言实现扫雷游戏初级版,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    Ja_king_8682021-12-15
  • C/C++C++实现LeetCode(309.买股票的最佳时间含冷冻期)

    C++实现LeetCode(309.买股票的最佳时间含冷冻期)

    这篇文章主要介绍了C++实现LeetCode(309.买股票的最佳时间含冷冻期),本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可...

    Grandyang9012021-12-10
  • C/C++C语言实现简单计算器功能(1)

    C语言实现简单计算器功能(1)

    这篇文章主要为大家详细介绍了C语言实现简单计算器功能的第一部分,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一...

    xiaocaidayong9942021-08-20
  • C/C++C++ STL array容器访问元素的几种方式

    C++ STL array容器访问元素的几种方式

    这篇文章主要介绍了C++ STL array容器访问元素的几种方式,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友...

    c语言中文网6522021-10-20
  • C/C++C++类URL编码和解码使用技巧

    C++类URL编码和解码使用技巧

    在项目开发过程中,经常会使用到c++ 的url编码和解码,本文将以此问题详细介绍使用技巧,需要的朋友可以参考下...

    C++教程网3122020-11-12
  • C/C++C++中使用function和bind绑定类成员函数的方法详解

    C++中使用function和bind绑定类成员函数的方法详解

    这篇文章主要介绍了C++中使用function和bind绑定类成员函数的方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友...

    dxgzg9882021-10-06
  • C/C++C++之BOOST字符串查找示例

    C++之BOOST字符串查找示例

    这篇文章主要介绍了C++之BOOST字符串查找的方法,实例演示了boost针对字符串的查找、判定及替换等操作,具有一定的实用价值,需要的朋友可以参考下...

    C++教程网10402021-02-18