首页 > 其他分享 >指针

指针

时间:2022-11-09 11:22:12浏览次数:34  
标签:10 int char ++ 数组 指针

什么是指针

C语言里,变量存放在内存中,而内存其实就是一组有序字节组成的数组,每个字节都有唯一的内存地址。
CPU 通过内存寻址对存储在内存中的某个指定数据对象的地址进行定位。数据对象是指在内存中的一个指定数据类型的数值或字符串,它们都有自己的地址,而指针就是保存这个地址的变量

所以得到:指针变量是一种保存变量地址的变量

指针:内存位置的直接地址
定义:存放变量的地址需要一种特殊类型(指针类型)的变量

为什么要使用指针

  • 指针的使用使得不同区域的代码可以轻易的共享内存数据,这样可以使程序更为快速高效
  • C语言中一些复杂的数据结构往往需要使用指针来构建,如链表,二叉树等
  • C语言是传值调用,但是有些操作传值调用是无法完成的,如通过被调函数修改调用函数的对象,但是这种操作可以由指针来完成,而且不违背传值调用

指针的声明

声明并初始化一个指针

数据类型(指针变量基类型)* 指针变量名;

例如:

int *p;        // 声明一个 int 类型的指针 p
char *p        // 声明一个 char 类型的指针 p
int *arr[10]   // 声明一个指针数组,该数组有10个元素,其中每个元素都是一个指向 int 类型对象的指针
int (*arr)[10] // 声明一个数组指针,该指针指向一个 int 类型的一维数组
int (*p)()     //声明一个指向函数的指针,该函数返回整型
int **p;       // 声明一个指针 p ,该指针指向一个 int 类型的指针

指针的声明比普通变量的声明多了一个一元运算符 ' * ' (取值操作符/间接寻址操作符)

注意:

声明一个指针变量并不会自动分配内存,在对指针进行间接访问之前,指针必须进行初始化,或是指向现有的内存,或者给他动态分配内存,否则我们不知道指针指向何处,这是一个很严重的问题

  1. 指针变量的值(指针变量中存放的值)是地址(即指针)

  2. 只有与指针变量基类型相同的变量才能存放到指针变量中

  3. 指针变量中只能存放地址

初始化操作如下:

/* 方法1:使指针指向现有的内存 */
int x = 1;
int *p = &x;  // 指针 p 被初始化,指向变量 x ,其中取地址符 & 用于产生操作数内存地址

/* 方法2:动态分配内存给指针 */
int *p;
p = (int *)malloc(sizeof(int) * 10);    // malloc 函数用于动态分配内存
free(p);    // free 函数用于释放一块已经分配的内存,常与 malloc 函数一起使用,要使用这两个函数需要头文件 stdlib.h

"&" 取址运算符和 "*" 取值运算符

这两个运算符的优先级别相同,按照自右向左方向结合

例如:

int a = 5;
int *p;
//这两个操作相同,都是取 a 的地址
p = &a;
p = &*p;  //先执行 *p 的操作,再取址

*&a的含义:

先进行 &a 运算,得到 a 的地址,再 ' * ' 取值运算,即就是变量 a 本身

未初始化和非法指针

如果一个指针没有被初始化,那么程序就不知道它指向哪里。它可能指向一个非法地址,这时,程序会报错。它也可能指向一个合法地址,实际上,这种情况更严重,你的程序或许能正常运行,但是这个没有被初始化的指针所指向的那个位置的值将会被修改。

NULL指针

NULL指针是一个特殊的变量,表示不指向任何东西。指针的地址为nil,即空值,也可以理解为0。在大多数的操作系统上,程序不允许访问地址为 0 的内存,因为该内存是为操作系统保留的。但是,内存地址 0 有一个特别重要的意义,它表明该指针不指向一个可访问的内存位置。

数组与指针

C语言中,数组名代表存放数组元素的连续存储空间的首地址,即指向数组中第一个元素的指针常量

指针变量既然可以指向变量,当然也可以指向数组元素(把某一元素地址放到一个指针变量中)

数组元素的指针就是数组元素的地址

一维数组与指针

定义一个指向数组的指针:

//数组的数据类型应与指针的基类型一致
int a[10];
int *p,*t;
t = a;  //指针指向数组首地址,即第一个元素
p = &a[2];  //指针指向数组第三个元素

数组元素的访问

下标法
int a[5],i;

for(i = 0;i < 5;i ++)
{
	printf("%d ",a[i]);
}
地址法
int a[5],i;

for(i = 0;i < 5;i ++)
{
	printf("%d ",*(a + 1));  //表示取出首地址元素后的第 i 个元素的内容,即下标为 i 的元素 a[i]
}
指针法
int a[5],i;
int *p;

for(p = a;p < (a + 5);p ++)
{
	//p ++是向前移动一个元素位置
	printf("%d ",*p)  
}

注:

  1. p + 1 是访问下一个地址,不存在赋值操作,不改变指针指向;p ++改变指针指向(有赋值操作),相当于是 p = p + sizeof ( 指针变量的基类型 )

  2. 虽然 a 和 p 的值都是数组的首地址,但是 a 不能像使用指针变量 p 那样执行自增自减的操作。因为 p 是指针变量,可以通过赋值操作改变它的值,使 p 指向数组中其他元素。数组名 a 是指针常量(代表一个地址常量),其值是不能改变的。

*p ++ , *(p ++) 和 (*p) ++ 的区别

"++" 和 " * " 为同级运算符,结合方向自右向左,所以 *p ++ 先取值,然后地址移一位,*(p ++) , p ++为一个整体,本身含义就是先运算后加一,运算指的是 p++作为一个整体与前面的 * 进行运算,所以 *p ++ = *(p ++)

(*p) ++ 先取值,值加一(地址不动)

只有 ++ (*p) , (*p) ++ , ++*p 指针位置不变,数据 ++,其他都是指针指向下一地址。

函数中改变数组元素的值(实参与形参的对应关系)

形参,实参都为数组
void f(int x[],int n)
{
	......
}

void main()
{
	int a[10];
	f(a,10);
}
实参为数组名,形参用指针变量
void f(int *a,int n)
{
	......
}

void main()
{
	int a[10];
	f(a,10);
}
实参,形参都用指针变量
void f(int *p,int n)
{
	......
}

void main()
{
	int a[10];
	int *p = a;
	f(p,10);
}
实参用指针变量,形参为数组名
void f(int x[],int n)
{
	......
}

void main()
{
	int a[10],*p = a;
	f(p,10);
}

指针数组和数组指针

指针数组是一个数组,数组中的每个元素都是指针

声明一个指针数组:
int *p[10];

[ ] 的优先级比 * 高,所以 p 先与 [ ] 结合,成为一个数组 p[ ] ; 再由 int * 指明这是一个 int 类型的指针数组,数组中的元素都是 int 类型的指针,即 p[ i ] 是一个指针

数组指针是一个指针,它指向一个数组

声明一个数组指针:
int (*p)[10];

由于 ( ) 的优先级最高,所以 p 是一个指针,指向一个 int 类型的一维数组,这一维数组的长度是10,这就是指针 p 的步长。也就是说,执行 p + 1 时, p 要跨过 n 个 int 型数据的长度

二维数组与指针

  1. a[ i ] 即 *( a + i ) 可以看成是一维数组的 a 的下标为 i 的元素。同时 a[ i ] 又可以看成是由 a[ i , 0 ] , a[ i , 1 ] , a[ i , 2 ] 这三个元素组成的一维整型数组的数组名,代表 a[ i , 0 ] 的地址,即 &a[ i , 0 ]

  2. a[ i , j ] 即 *( *(a + i) + j ) 代表这个数组中下标为 j 的元素地址,即&a[ i , j ]

  3. a[ i , j ] ——> *( a[ i ] + j ) ——> *( *(a + i) + j ) ——> *(a + i)[ j ]

如上图,可定义:
int (*p)[3]; // 数组指针

字符串与指针

例如:

char *p = "hello";
或者
char *p;
p = "hello";

将保存在常量储存区中的 " hello " 的首地址赋给指针变量 p (不可修改)
[[字符串#注意:]]

将字符串 a 复制给字符串 b

下标法

//*(a + i) 为 a[i]
for(i = 0;*(a + i) != '\0';i ++)
{
	b[i] = a[i]; //或者 *(b + i) = *(a + i);
}
b[i] = '\0'; //或者*(b + i) = '\0'(字符串结束的标志)

指针法

char a[] = "hello";
char b[40],*p1,*p2;

p1 = a;
p2 = b;

for(;*p1 != '\0';p1 ++,p2 ++)
{
	*p2 = *p1;
}
*p2 = '\0';

函数调用实现字符串的复制

字符数组作参数
void copy_str(char from[],char to[])
{
	int i = 0;
	while(from[i] != '\0')
	{
		to[i] = from[i];
		i ++;
	}
	to[i] = '\0';
}
字符指针变量作形参
//实参传入时,应为字符数组,字符指针指向的字符是常量存储区中的字符串,不可修改
void copy_str(char *from,char *to)
{
	while((*to ++ = *from ++) != '\0');
}

字符数组和指针变量的区别

1. 组成成分

字符数组由若干个元素组成,每个元素中放一个字符,而字符指针变量中存放的是地址(字符串第一个字符的地址),不是将字符串放到字符指针变量中

2.赋值

字符数组
  1. 定义的时候直接用字符串初始化
    char a[10] = "hello";

注意:

不能先定义再对数组进行赋值,例如 :
char a[10]; a[10] = "hello"; //这样是错误的
只有定义初始化时才能这样赋值

  1. 对数组中字符逐个赋值
    char a[10] = {'h','e','l','l','o'};

  2. 利用 strcpy(比较推荐)
    char a[10]; strcpy(a,"hello");

易错情况
  1. char a[10]; a[10] = "hello";,一个字符不能容纳一个字符串,且 a [10] 不存在
  2. char a[10]; a = "hello";,a 虽然是指针,但是它已经指向再堆栈中分配的十个字符空间,现在这个情况 a 又指向数据区的 hello 常量,这里的指针 a 出现混乱,不允许 ! 会出现 const char 无法转换为 char 类型
字符指针变量

char *s = "hello"; == char *s; s = "hello";

函数与指针

函数指针(指向函数的指针)

一个函数在编译时被分配给以个入口地址,函数指针指向函数的入口地址

声明一个函数指针:
返回值类型 (*指针变量名)([形参列表]);

注意!!!

指针变量名要用括号括起来,不可省略,否则:
int *p(void *,void *);
表明 p 是一个函数,返回一个指向 int 类型的指针(指针函数)

例如:从键盘读入两行字符串,判断二者是否相等

#include "stdio.h"
#include "string.h"

int str_comp(const char *m,const char *n);                             // 声明一个函数 str_comp,该函数有两个 const char 类型的指针,函数的返回值为 int 类型
void comp(char *a,char *b,int (*prr)(const char *,const char*));       // 声明一个函数 comp ,注意该函数的第三个参数,是一个函数指针

int main()
{
    char str1[20];      // 声明一个字符数组
    char str2[20];
    int (*p)(const char *,const char *) = str_comp;            // 声明并初始化一个函数指针,该指针所指向的函数有两个 const char 类型的指针,且返回值为 int 类型
    gets(str1);         // 使用 gets() 函数从 I/O 读取一行字符串
    gets(str2);
    comp(str1,str2,p);  // 函数指针 p 作为参数传给 comp 函数

    return 0;
}

int str_comp(const char *m,const char *n)
{
   // 库函数 strcmp 用于比较两个字符串,其原型是: int strcmp(const char *s1,const char *s2);
    if(strcmp(m,n) == 0) 
        return 0;
    else
        return 1;
}

/* 函数 comp 接受一个函数指针作为它的第三个参数 */
void comp(char *a,char *b,int (*prr)(const char *,const char*))
{
    if((*prr)(a,b) == 0)
        printf("str1 = str2\n");
    else
        printf("str1 != str2\n");
}

指针函数

指针函数的本质是函数,函数返回值是一个地址值(用于需要地址的情况)

指针函数的声明:
返回值类型 *指针变量名([形参列表]);

指向指针的指针(多级间接寻址)

指向指针的指针的定义
char **p;('*'运算符自右向左)
相当于*(*p)

例如:

char *n[] = {"hello","world","chana"};
char **p;
for(int i = 0;i < 3;i ++)
{
	p = n + i; //指向下一个元素
	printf("%s",*p);
}

输出结果:
hello world chana

标签:10,int,char,++,数组,指针
From: https://www.cnblogs.com/ttduck/p/16873014.html

相关文章

  • 实验四 类与数组、指针
    任务五代码:vectorInt:#pragmaonce#include<iostream>#include<cassert>usingnamespacestd;classVectorInt{public:VectorInt(ints);......
  • 指针
    数据存放于内存地址中,而指针指的就是内存地址。变量的指针:存放变量数据的内存地址。指针变量:存放某一数据的地址的变量。指针变量的定义:数据类型*变量名;eg:int*amq;指针......
  • 实验4 类与数组、指针
    实验任务5:#include<iostream>#include"vectorInt.h"usingnamespacestd;voidtest(){usingnamespacestd;intn;cin>>n;vectorIntx1(n);......
  • 实验四 类与数组、指针
    实验五:task5.cpp#include<iostream>#include"vectorInt.hpp"voidtest(){usingnamespacestd;intn;cin>>n;vectorIntx1(n);for(autoi......
  • 实验4 类与数组、指针
    实验五vectorint.h#pragmaonce#include<iostream>usingnamespacestd;classvectorInt{public: ints,*p; vectorInt(intm) { p=newint[m]; s......
  • 实验4 类与数组、指针
    Task1~4浅复制:inta=10;intb=a;可用于静态内存的复制。对于简单的类,默认的复制构造函数已经够用了,但当类持有其他资源,如动态分配的内存、指针等,就需要用到深复制......
  • 实验四类和对象数组及指针
    11#pragmaonce22#include<iostream>3344usingstd::cout;55usingstd::endl;6677classvectorInt{88public:99//构造......
  • 湍流数组 双指针+数学 奇偶分数组 字符串
    978.最长湍流子数组dp[0][0]=dp[0][1]=1;初始化一个数for(inti=1;i<n;i++){dp[i][0]=dp[i][1]=1;if(arr[i]>arr[i-1]){dp[i][0]=dp[i-1][1......
  • C语言指针
    在C语言中,有两种方式得到一个变量方式一:直接找到变量方式二:间接找到变量。也就是先找到变量的地址,然后再根据地址解析得到该变量。......
  • AcWing 3583 整数分组(01背包 + 双指针)
    原题链接本题是比较明显的01背包,选或者不选,中间可以用双指针找到最后可以选到的区间长度,那么如果选当前最后一个区间的话最后就要求这个区间前面的长度要最大状态表示:f[......