07 指针基础

1.指针的基本概念

指针(pointer)描述了数据在内存中的位置,标示了一个占据存储空间的实体,在这一段空间起始位置的相对距离值。

指针相对于一个内存单元来说,指的是单元的地址,该单元的内容里面存放的是数据。在C语言中,允许用指针变量来存放指针,因此,一个指针变量的值就是某个内存单元的地址或称为某内存单元的指针

  • 每个变量都被存放在从某个内存地址(以字节为单位)开始的若干个字节中
  • “指针”,也被称为“指针变量”,大小为4个或8个字节的变量,其内容代表一个内存地址
  • 通过指针,能够对该指针指向的内存区域进行读写
  • 如果把内存的每个字节都想象为宾馆的一个房间,那么内存地址相当于房间号,而指针里存放的,是房间号

2.指针变量及基本运算

2.1 指针变量

指针变量是存放一个内存地址的变量,不同于其他类型变量,它是专门用来存放内存地址的,也称为地址变量。定义指针变量的一般形式为:类型说明符*变量名

  • 初始化

    int a;
    int *p = &a;

  • 赋值

    int a;
    int *p;
    *p = &a;

首先来研究一下星号 *

  • 这是间接运算符中的星号

与乘法运算符 * 不同,间接运算符 * 是一元运算符,也就是说,间接运算符只有一个操作数。

在下例中,p 指向变量 x。因此,表达式 *p 等效于变量 x 。

double x, y, *p;    // 两个 double变量和一个 double指针
p = &x;             // 使得 p 指向 x
*p = 1;             // 对变量 x 赋值 1
*p *= 2;            // 将 x 乘以 2
y = *p + 3;       // 将加法 x+3 的结果赋值给 y
  • 这是声明中的星号

int *p; //p是一个指针,变量p的类型是int*
char *pc; //pc是一个指针,变量pc的类型是char*
float *pf; //pf是一个指针,变量pf的类型是float*

间接运算符必须匹配一个具有有效地址的指针。也就是说需要p=&x为p分配一个有效地址,否则程序可能会崩溃。这也称解引用一个没有定义的指针。

还有一些需要注意的:

一个指针变量,其本身也是内存中的一个对象,也就是说,其他指针可以指向该指针。若想创建指针的指针,必须使用两个星号。

char c = 'A', *cp = &c, **cpp = &cP;//正确操作!

不能通过多次使用地址运算符 & 来取得指针的值。

char c = 'A', **cpp = &(&c);//错误示范!

不允许把一个数赋予指针变量。

int *p;p=1000; //这是非法的

被赋值的指针变量前不能再加“*”说明符

*p = &a;//这样也不对

形参为指针变量时,因为值传递,改变形参不代表改变实参。

字符串指针指向的是一个字符串。

char *str = "This is a string.";
//此时,字符指针指向的是一个字符串常量的首地址,即指向字符串的首地址。
char string[ ]="This is a string.";
//此时,string是字符数组,它存放了一个字符串

函数指针的移动毫无意义,函数指针变量不能进行算术运算.

2.2 指针的运算

  • 指针的算数运算
    • 指针的每一次递增,它其实会指向下一个元素的存储单元。

    • 指针的每一次递减,它都会指向前一个元素的存储单元。

    • 指针在递增和递减时跳跃的字节数取决于指针所指向变量数据类型长度,比如 int 就是 4 个字节。

      #include <stdio.h>
      int main()  
      {  
      int *p = (int *)1000;  
      printf("%d\n",p+5);//1020  
      printf("%d\n",(short *) p+5);//1010  
      printf("%d\n",(unsigned long *)p+5);//1020  
      printf("%d\n",(double *)p+5);//1040  
      printf("%d\n",(char ***)p+5);//1020  
      printf("%d\n",(char *)p+5);//1005  
      printf("%d\n",(long long)p+5);//1005  
      return 0;  }
      

3.指针作为函数参数

先了解一下什么是 形参(parameter)、实参(argument)

当调用一个函数时,可以不向函数传入参数或者向函数里传入参数。当向函数里传入参数时,调用函数时传入的参数为实参,在函数里传入的参数为形参。

举例:

在这个程序中,a为形参,b为实参。

int f(int a){
	printf("a=%p\n",&a);
	a++;
	return 0;
}

#include <stdio.h>
int main(){
	int b=1;
	f(b);
	printf("b=%p\n",&b);

	return 0;
}

当函数调用时,形参用来接收实参,相当于实参的拷贝,然后在计算机内存中分配地址。
实际上形参的地址与实参的地址不相同,所以当形参发生改变时,实参并不会发生改变。

那么,如何使得实参随形参变化而变化?答案是指针。

include <stdio.h>

int f(int *a){
	(*a)++;
	return 0;
}


int main(){

	int b=1;
	printf("%d\n",b);
	f(&b);
	printf("%d\n",b);

	return 0;
}

4.指针与数组

4.1 用指针引用数组元素

引用数组元素除了使用下标,还可以利用指针,过指向某个数组元素的指针变量来引用数组元素。

数组包含若干个元素,元素即变量,变量都有地址。所以每一个数组元素在内存中都占有存储单元,都有相应的地址。

指针变量既然可以指向变量,当然也就可以指向数组元素。同样,数组的类型和指针变量的基类型一定要相同。

#include <stdio.h>
int main(void)
{
	int a[] = {1, 2, 3};
	int *p = &a[0];
	int *q = a;
	printf("*p = %d, *q = %d\n", *p, *q);
	return 0;
}

p=&a[0] 和 p=a 等价。

4.2 指针的移动

如果指针变量 p 已经指向一维数组的第一个元素,那么 p+1 就表示指向该数组的第二个元素。

p+1 不是指向下一个地址,而是指向下一个元素。

比如 int 型数组中每个元素都占 4 字节,每字节都有一个地址,所以 int 型数组中每个元素都有 4 个地址。如果指针变量 p 指向该数组的首地址,那么“下一个地址”表示的是第一个元素的第二个地址,即 p 往后移一个地址。而“下一个元素”表示 p 往后移 4 个地址,即第二个元素的首地址。

# include <stdio.h>
int main(void)
{
	int a[] = {1, 2, 3,};
	int *p = a;
	printf("p = %d, p + 1 = %d\n", p, p+1);    //用十进制格式输出
	printf("p = %#X, p + 1 = %#X\n", p, p+1);  //也可以用十六进制格式输出
	printf("p = %p, p + 1 = %p\n", p, p+1);    //%p是专门输出地址的输出控制符
	return 0;
}

不同类型的数组,p+1 移动的地址的个数不同,但都移向下一个元素。

如果 p 指向的是第一个元素的地址,那么 *p 表示的就是第一个元素的内容。
同样,p+i 表示的是第 i+1 个元素的地址,那么 *(p+i) 就表示第 i+1 个元素的内容。
即 p+i 就是指向元素 a[i] 的指针,*(p+i) 就等价于 a[i]。

*(q+i) 两边的括号不能省略。因为 *q+i 和 *(q+i)是不等价的。指针运算符 “*” 的优先级比加法运算符“+”的优先级高。所以 q+i 就相当于 (q)+i 。

# include <stdio.h>
int main(void)
{
	int a[] = {1, 2, 3, 4, 5};
	int *p, *q, *r;
	p = &a[3];
	printf("*p = %d\n", *p);
	q = a;
	q = q + 3;
	printf("*q = %d\n", *q);
	r = a;
	printf("*(r+3) = %d\n", *(r+3));
	return 0;
}

5.指针与字符串

字符串可以看作一种特殊的数组:

//字符数组
int main(){
  char str[] = "hello world";   //数组内:h e l l o   w o r l d \0
  printf("%s\n",str);
  return 0;
}
//指针数组
int main(){
  char *str = "hello world";  //定义了一个字符常量,字符串中的内容无法改变 h e l l o   w o r l d \0
  printf("%s\n",str);   //此时,str指向了字符常量的首地址,通过字符指针的移动,遍历输出整个字符串
  return 0;
}
//%s占位符的特点就是只要告诉它字符串的首地址,就可以读取整个字符串

二者的区别是:

储存内容 赋值方式 分配内存
字符数组 由若干个元素组成,每个元素中放一个字符 只能对各个元素赋值,或者:char str[14] = "hello world"; 编译时分配内存单元,有确定的地址
字符指针变量 存放的是字符串第一个字符的地址 char *a = "hello world"; 定义字符指针变量时,分配内存单元,在其中可以放一个字符变量的地址

参考:
知乎 c语言入门 第十六章 指针和字符串(指针数组) 知乎用户ZBaJok
CSDN 字符指针与字符串 mensaochun

posted @ 2022-11-19 14:15  许悠  阅读(61)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end
1 2 3
4