C语言指针初进阶知识汇总

简介: C语言指针初进阶知识汇总

1 指针

1.1 指针是什么

指针(pointer):一个值为内存地址的变量。


char 类型变量的值是字符,int 类型变量的值是整数,指针变量的值是地址。


1.2 指针的声明

数据类型 *指针名,这里的 * 表明声明的变量是一个指针,没有访问指针目标的含义


int * pi; // pi是指向int类型变量的指针
char * pc; // pc是指向char类型变量的指针
float * pf; // pf是指向float类型变量的指针

指针存放的值是一个地址,在大部分系统内部,该地址由一个无符号整数表示,但不能把指针认为是整数类型。


指针变量不能赋予常量值

不能接受键盘输入的数据

1.3 运算符

间接运算符 *:访问地址的目标


val = *ptr; // 找出ptr指向的值

地址 & 运算符:取变量的存储地址


&num 表示变量 num 的地址。

1.4 简单的小例子们

#include <stdio.h>
int main ()
{
   int  num = 97;   
   int  *p;        // 指针变量的声明 
   p = &num;       //在指针变量中存储 num 的地址 
   printf("Address of var variable: %p\n", &num );
   // 指针变量中存储的地址 
   printf("Address stored in ip variable: %p\n", p );
   // 使用指针访问值 
   printf("Value of *ip variable: %d\n", *p );
   return 0;
}


结果:


20191102171259819.png


图解

20191102180016557.png


1.5 指针的运算

知识点:


三个操作符的优先级:() > [] > *,没错 * 是最卑微的

*p++ 里,* 号和 ++ 属于同一优先级,且方向都是从右向左的,*p++ 和 *(p++) 作用相同。


1.5.1 指针加减运算

20191102214908987.png


#include<stdio.h>
void main(){
  int a=10,b=20,c=30;
  int *p=&b;
  printf("%p\n",&a);
  printf("%p\n",&b);
  printf("%p\n",&c);
  p=p+1;
  printf("%p\n",p);
  p=p-2;
  printf("%p\n",p); 
} 

结果:

20191102214130262.png


1.5.2 间址运算


20191105070527484.png


*p++:* 与 ++ 同优先级,结合方向为自右向左,p++,先赋值再自加,因此结果等价于 *p 返回其值,p++


*++p:++p,先自加后赋值,等价于 p++, *p 返回其值


(*p)++:括号优先,等价于 *p 返回其值,然后将 *p 的结果 + 1


#include<stdio.h>
int main(){
  int a[] = {10,20,30}, *p=a; 
  printf("*p++ is %d\n", *p++);   
  p=a; 
  printf("*++p is %d\n", *++p); 
  p=a; 
  printf("++(*p) is %d\n", ++(*p)); 
  p=a;
  printf("a[0] is %d\n",a[0]);
  printf("(*p)++ is %d\n", (*p)++); 
  return 0;
}

结果:


20191106070729615.png


两个指针相减


首先必须明确,通常只有当两个指针指向同一个数组时才对两个指针相减。


两个指针相减得到的整数是这两个指针所指向的元素索引值之差。


例如有如下代码:


int a[5] = {10, 20, 30, 40, 50};


int *p1 = a;


int *p2 = &a[3];


int x = p2 - p1;


所以表达式 p2 - p1的值是3


2 指针与数组

2.1 指针和一维数组

2.1.1 定义数组及指针变量

20191104071234142.png


数组名 s 代表的是该数组首元素的首地址,&s 代表的是整个数组的首地址


s == &s[0],返回 ture


&s == &s[0], 是错误的比较,尽管 &s 与 &s[0] 的地址值相同,但两者的含义不同



2.1.2 能动手就不要瞎扯

移动指向数组的指针


20191104071349282.png


数组元素的引用方法:


20191104071511560.png


因为 ps 是变量,s 是符号常量,不能给 s 赋值, s = ps(×)


#include<stdio.h>
int main(){
  int i;
  int a[5] = {1,2,3,4,5}, *p = a;   // p作为一维数组 a 的指针
  // 通过 a[i] 方式输出所有元素
  for(i=0; i<5; i++){
    printf("%d ",a[i]);
  } 
  printf("\n");
  // 通过 *(a+i)方式输出所有元素
  for(i=0; i<5; i++){
    printf("%d ",*(a+i));
  }
  printf("\n");
  // 通过 *(p+i)方式输出所有元素,p不移动 
  for(i=0; i<5; i++){
    printf("%d ",*(p+i));
  }
  printf("\n");
  printf("a is %p\n", a);
  printf("p is %p\n", p);
  // 通过 *p方式输出所有元素,p移动 
  for(i=0; i<5; i++){
    printf("%d ",*p++);
  }
  printf("\n");
  printf("%p", p);
}

结果:


20191109122228153.png


2.2 指针和二维数组

参考:C/C++二维数组总结


2.2.1 定义

(1) 二维数组定义


定义了一个Array[3][4],那就指的是定义了一个三行四列的矩阵形状的二维数组,如下图所示。这样的矩阵在内存中是以箭头右边的方式存放的,也就是说实际上我们定义的二维数组在内存中仍然像是一维数组那样连续存储的。可以想象为把一个矩阵一层层伸展铺平。


20191109094238513.png


int array[3][4];   // 内含 int 数组的数组


数组名 array 是该数组首元素的地址,一维数组首元素的地址,而 array[0] 本身是一个内含 4 个整数的数组,


array = &array[0]。


&array 是整个数组的首地址。


array,&array,&array[0],&array[0][0],虽然地址值相同,但有各自的含义。

例子:

#include<stdio.h>
int main(){
  int array[3][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}};
  printf("array is %d\n",array);
  printf("*array is %d\n",*array);
  printf("array[0] is %d\n",array[0]);
  printf("*array[0] is %d\n",*array[0]);
  printf("array[0][0] is %d\n",array[0][0]);
  printf("&array[0][0] is %d\n",&array[0][0]);
}

结果:

20191109102810750.png


(2) 二维数组指针定义


基类型 (*指针变量)[整形表达式]


int a[2][3], (*p)[3]  = a;


() 优先级高,首先说明p是一个指针,指向一个整型的一维数组,这个一维数组的长度是 n,也可以说是 p 的步长。执行 p+1 时,p 要跨过 n 个整型数据的长度。


p[i]:指向 a[i] 的元素


p++:指向数组 a 的后一个一维数组元素


p--:指向数组 a 的前一个一维数组元素


p+1:等价于 a+1


当 p 指向 a 数组的开头时,可以通过以下形式来引用 a[i][j]


*(p[i]+j):对应于 *(a[i]+j)


*(*(p+i)+j):对应于 *(*(a+i)+j)


*(*(p+i))[j]:对应于 *(*(a+i)+j)


p[i][j]:对应于 a[i][j]


数组指针 p 与对应的二维数组 a 的区别是:二维数组 a 是一个常量,数组指针 p 是一个变量


2.2.2 能动手就不要瞎扯


#include<stdio.h>
void main(){
  int i, j;
  int a[2][3] = {{1,2,3},{4,5,6}}, (*p)[3] = a;
  // 通过 a[i][j] 方式输出所有元素 
  for(i=0; i<2; i++)
    for(j=0; j<3; j++)
  printf("%d ",a[i][j]);  
  printf("\n"); 
  // 通过 *(*(a+i)+j) 方式输出所有元素 
  for(i=0; i<2; i++)
    for(j=0; j<3; j++)
  printf("%d ",*(*(a+i)+j));  
  printf("\n");
  // 通过 *(p[i]+j) 方式输出所有元素 
  for(i=0; i<2; i++)
    for(j=0; j<3; j++)
  printf("%d ",*(p[i]+j));  
  printf("\n");
  // 通过 *(*(p+i)+j) 方式输出所有元素 
  for(i=0; i<2; i++)
    for(j=0; j<3; j++)
  printf("%d ",*(*(p+i)+j));  
  printf("\n");
}

3 指针数组

3.1 定义

存储指针的数组。它的每个元素都是指针变量,指向相同的数据类型。


数组类型 *指针数组名[元素个数];


int *p[3];


[] 比 * 的优先级高,p 先与 [3] 结合,形成 p[3] 的数组形式,它有 3 个元素,然年再与 * 结合,表示是指针类型的数组。


int *p[3] 表示一个一维数组内存放着三个指针变量,分别是p[0]、p[1]、p[2]

20191111222543139.png

20191111222605307.png


使用指针数组更方便地操作长短不一的字符串,而使用二维数组来操作字符串相对浪费空间,如下:

20191111222803124.png


3.2 灵活的双手动起来


#include <stdio.h>
const int MAX = 3;
int main ()
{
   const char *words[] = {"wyd","is","smart"};
   int i = 0;
   for ( i = 0; i < MAX; i++)
   {
      printf("Value of words[%d] = %s\n", i, words[i] );
   }
   return 0;
}

4 多级指针

二级指针:就是对一级指针再取地址


三级指针:就是对二级指针再取地址


#include <stdio.h>
const int MAX = 3;
void main ()
{
   int *p, **pp, ***ppp, s = 100;
   p = &s;
   // q 存 p 的地址,p 存 s 的地址
   pp = &p;
   ppp = &pp;
   printf("Value of p = %d\n", *p); 
   printf("Value of pp = %d\n", **pp);
   printf("Value of ppp = %d\n", ***ppp);
}


结果


20191112071228531.png

5 字符指针与字符串

5.1 定义

指向 char 型数据的指针

20191113205525517.png

20191113210359117.png

20191113210918387.png

20191113211202408.png

20191113211304935.png

5.2 打代码

#include <stdio.h>
void main ()
{
  char s [] = "wyd is smart";
  char *p = "wyd is handsome";
  *(s+3) = 0; // 在第4个字符位置加结束标志
  puts(s); 
  puts(p);
  //*(p+3)=0; 运行报错,字符串常量不能赋值 
  p=s;   // 没有这行,下面输入字符回车会报错 
  printf("%p",p); 
  gets(p);
  puts(p);
}

20191113214225864.png


6 指针型函数

6.1 定义

函数返回值的数据类型决定了该函数的数据类型。


数值型函数:返回值为数值类型


字符型函数:返回值为字符类型


指针型函数:返回值是地址


数据类型 *函数名(参数)


如:int *func(int x,float y)


6.2 敲代码

例 1

#include <stdio.h>
int *f(int *x, int *y){// x与y都是指针变量,存的地址
    if(*x < *y)
        return x;
    else
        return y;
}
void main ()
{
    int x=7, y=8, *p;
    p=f(&x, &y);
    printf("%p\n", p);
    printf("%d\n", *p);
}


结果:

2020050422143537.png

#include <stdio.h>
char *f(char *b){
  b += 3;
  return b;
}
void main ()
{
  char a[] = "abcdefg", *p;
  p = f(a);
  printf("%s\n",p);
}

7 函数指针(指向函数)

7.1 定义

C 语言中,指针变量除了保存数据的存储地址外,还可以保存函数的存储首地址。函数的存储首地址又称为函数的执行入口地址。


数据类型 (*函数指针名)();


如:int (*func)();                                                                                                                                


7.2 码代码

#include <stdio.h>
int f1(int x){
  return x*x;
}
int f2(int x){
  return x*x*x;
}
// f1 和 f2 形参均为函数指针,存放函数的首地址
int f(int (*f1)(), int (*f2)(), int x){ 
  return f2(x) - f1(x);
}
void main(){
  int i;
  i = f(f1, f2, 2);
  printf("%d\n",i);
}

结果: 4  


8 结构体指针

8.1 结构体变量指针

8.1.1 定义

指向结构体变量的指针

struct 结构体类型名 *结构体指针名;
struct Student st,*p = &st; // p 是指向结构体变量 st 首地址的指针

p 不是结构体变量,不能写成 p.age,要写成(*p).age,或 p -> age

-> 的优先级最高,如:p->age+1,相当于(p->age)+1,即返回 p->age 的值+1

8.1.2 干活

#include<stdio.h> 
struct Student{
  int no;
  char name[20];
}st={101,"apple"},*p;
int main(){
  p = &st;
  printf("num=%d,name=%s\n",st.no,st.name);
  printf("num=%d,name=%s\n",(*p).no,(*p).name);
  printf("num=%d,name=%s\n",p->no,p->name);
}

结果:


201912071403102.png


8.2 结构体数组指针

8.2.1 定义

指向结构体数组,即将该数组的起始地址赋值给该指针变量。


struct Student  s[40], *p=s;


8.2.2 搬砖

(1)第一回合


#include<stdio.h> 
struct Student{
  int no;
  char name[20];
}st[2]={{101,"apple"},{102,"peach"}}, *p;
int main(){
  p = &st;
  printf("num=%d,name=%s\n",p->no,p->name);
  printf("num=%d,name=%s\n",(p++)->no,p->name); // 先取得p->no的值,再进行p自增1,指向下一个元素st[1] 
  printf("num=%d,name=%s\n",p->no,p->name);
}

结果:

20191207140456124.png


(2)第二回合


#include<stdio.h> 
struct Student{
  int no;
  char name[20];
}st[2]={{101,"apple"},{102,"peach"}}, *p;
int main(){
  p = &st;
  printf("num=%d,name=%s\n",p->no,p->name);
  printf("num=%d,name=%s\n",(++p)->no,p->name); // 先进行p自增1,指向下一个元素st[1] ,再取成员 no 的值 
}

结果:


20191207140531581.png


本章终!  


相关文章
|
1月前
|
存储 C语言
【C语言篇】深入理解指针3(附转移表源码)
【C语言篇】深入理解指针3(附转移表源码)
36 1
|
24天前
|
C语言
【c语言】指针就该这么学(1)
本文详细介绍了C语言中的指针概念及其基本操作。首先通过生活中的例子解释了指针的概念,即内存地址。接着,文章逐步讲解了指针变量的定义、取地址操作符`&`、解引用操作符`*`、指针变量的大小以及不同类型的指针变量的意义。此外,还介绍了`const`修饰符在指针中的应用,指针的运算(包括指针加减整数、指针相减和指针的大小比较),以及野指针的概念和如何规避野指针。最后,通过具体的代码示例帮助读者更好地理解和掌握指针的使用方法。
45 0
|
23天前
|
C语言
【c语言】指针就该这么学(3)
本文介绍了C语言中的函数指针、typedef关键字及函数指针数组的概念与应用。首先讲解了函数指针的创建与使用,接着通过typedef简化复杂类型定义,最后探讨了函数指针数组及其在转移表中的应用,通过实例展示了如何利用这些特性实现更简洁高效的代码。
15 2
|
24天前
|
C语言
如何避免 C 语言中的野指针问题?
在C语言中,野指针是指向未知内存地址的指针,可能引发程序崩溃或数据损坏。避免野指针的方法包括:初始化指针为NULL、使用完毕后将指针置为NULL、检查指针是否为空以及合理管理动态分配的内存。
|
24天前
|
C语言
C语言:哪些情况下会出现野指针
C语言中,野指针是指指向未知地址的指针,通常由以下情况产生:1) 指针被声明但未初始化;2) 指针指向的内存已被释放或重新分配;3) 指针指向局部变量,而该变量已超出作用域。使用野指针可能导致程序崩溃或不可预测的行为。
|
30天前
|
存储 C语言
C语言32位或64位平台下指针的大小
在32位平台上,C语言中指针的大小通常为4字节;而在64位平台上,指针的大小通常为8字节。这反映了不同平台对内存地址空间的不同处理方式。
|
30天前
|
存储 算法 C语言
C语言:什么是指针数组,它有什么用
指针数组是C语言中一种特殊的数据结构,每个元素都是一个指针。它用于存储多个内存地址,方便对多个变量或数组进行操作,常用于字符串处理、动态内存分配等场景。
|
30天前
|
存储 C语言
C语言指针与指针变量的区别指针
指针是C语言中的重要概念,用于存储内存地址。指针变量是一种特殊的变量,用于存放其他变量的内存地址,通过指针可以间接访问和修改该变量的值。指针与指针变量的主要区别在于:指针是一个泛指的概念,而指针变量是具体的实现形式。
|
30天前
|
C语言
C语言指针(3)
C语言指针(3)
11 1
|
30天前
|
C语言
C语言指针(2)
C语言指针(2)
13 1