【C语言】指针

【C语言】指针:

  • 指针是个变量,存储其它变量的内存地址。
  • 使用 "&" 获取内存地址,使用 "*" 获取内存地址中的值。
  • 指针可以作为参数传入函数,也可以作为函数的返回值。
  • 指针也可以指向数组、指针、函数等。
  • 指针也可以作为数组的元素,即数组中元素是指针。

1、指针变量

声明指针变量:基类型 *指针变量名**;**

指针指向内存地址:指针变量名 = 内存地址**;**

通过指针获取指向的内存地址中的值:*指针变量名**;**

注:基类型是指针指向内存地址中的值的类型。

cs 复制代码
#include <stdio.h>

int main(void)
{
    int a = 10;
    printf("a: address is %p, value is %d\n", &a, a);
    int *p;
    p = &a;
    printf("p point to a's address is %p, a's value is %d\n", p, *p);
    return 0;
}

// 结果:
a: address is 000000000061FE14, value is 10
p point to a's address is 000000000061FE14, a's value is 10

2、指针作为函数参数

某个值作为参数传入函数,则实际传入的是值的拷贝,函数结束就销毁,不会修改值本身。

若是指针为函数参数,则操作的是指针指向的内存地址。常举的例子是两个值互换。

cs 复制代码
#include <stdio.h>

// function prototype 
void swap_1(int, int);                  // 参数是值
void swap_2(int *, int *);              // 参数是指针
 
int main(void)
{
    int x = 1, y = 2;                    // 局部变量x,y, 只在该函数内有效
    printf("x = %d, y = %d\n", x, y);
    
    swap_1(x, y);                        // x,y作为参数传入函数,实际传入的是x,y的拷贝
    printf("Argument are values. swapped: x = %d, y = %d\n", x, y);
    
    
    swap_2(&x, &y);                      // &x是x的内存地址, 作为参数时可视为指向x的指针
    printf("Argument are pointers. swapped: x = %d, y = %d\n", x, y);
    return 0;
}
 
void swap_1(int a, int b)
{
    int tmp = a;                         // 局部变量tmp,a,b,只在该函数内有效
    a = b;
    b = tmp;
}

void swap_2(int *a, int *b)
{
    int tmp = *a;
    *a = *b;
    *b = tmp;
}
 
// 结果:
x = 1, y = 2
Argument are values. swapped: x = 1, y = 2  
Argument are pointers. swapped: x = 2, y = 1

3、指针作为函数返回值

即函数返回的是指针。

cs 复制代码
int *func(void)              // 函数原型

int main(void)
{
    int *p;
    p = func();              // 接收函数返回的指针
    ...
    return 0;
}

int *func(void)
{
    int *pointer;
    ...
    retrun pointer;          // 函数返回指针
}

4、指针的运算

① 指针 指向的内存地址是用数值表示的,可以做算术运算(++、--、+、-)。

指针的算术运算只能和整数进行加减。一般用在指针指向数组(即数组第一个元素的内存地址),通过指针的递增或递减,来指向下一个或上一个元素。

  • 指针的每一次递增,是指向下一个元素的内存地址。
  • 指针的每一次递减,是指向上一个元素的内存地址。
  • 指针的每一次递增/递减的跳跃的字节数取决于基类型的长度。例如:int是4字节,指向下一个int是指向4字节以后的内存地址。

② 两指针之间也能进行减法,计算两指针指向的内存地址之间间隔多少元素。一般是指向同一个数组的不同元素,来计算数组有多少个元素。

③ 两指针之间也能进行比较(==、< 、 >等),判断两指针指向的内存地址的大小,返回值是整数(1:True,0:False)。

cs 复制代码
#include <stdio.h>

int main(void)
{
    int a[] = { 2024, 4, 23, 9, 20 };
    int *p, *q, m;
    p = q = a;           // ① 指针p和指针q都指向数组a(第一个元素的内存地址)

    printf("p point to address %p, q point to address %p, the first element address is %p\n", p, q,  &a[0]);
    q++;                 // ② 指针q指向下一个元素的内存地址,相当于q=q+1
    printf("the second element: address is %p, %p\n", q, &a[1]);
    q += 3;              // ③ 指针q指向跨越3个元素的内存地址,相当于q=q+3
    printf("the last element: address is %p, %p\n", q, &a[4]);
    m = q - p + 1;      // 指针q指向的内存地址与指针p指向的内存地址之间间隔多少元素
    printf("%d elements are spaced from p to q\n", m);
    printf("if q point to address > p point to address(1:True,0:False): %d\n", q > p);   // 比较p,q指向的内存地址的大小

    return 0;
}

// 结果:
p point to address 000000000061FDF0, q point to address 000000000061FDF0, the first element address is 000000000061FDF0
the second element: address is 000000000061FDF4, 000000000061FDF4
the last element: address is 000000000061FE00, 000000000061FE00
5 elements are spaced from p to q
if q point to address > p point to address(1:True,0:False): 1

通过指针获取值:*指针。

cs 复制代码
#include <stdio.h>

int main(void)
{
    int a[] = { 2024, 4, 23, 9, 20 };
    int *p, m;
    p = a;           // 指针p指向数组a(第一个元素的内存地址)

    printf("p point to a,the value is %d, the first element is %d\n", *p, a[0]);
    p++;            // 指针p指向数组下一个元素
    printf("next value is %d, the second element is %d\n", *p, a[1]);

    return 0;
}

// 结果:
p point to a,the value is 2024, the first element is 2024
next value is 4, the second element is 4

注意区分 *p++, *++p , (*p)++, ++*p, p++, ++p:

*p++:先获取指针指向的内存地址中的值(*p),再指针指向下一个内存地址(p=p+1)。

*++p:先指针指向下一个内存地址(p=p+1),再获取指向的内存地址中的值(*p)。

(*p)++:先获取指针指向的内存地址中的值(*p),再将该值+1(*p=*p+1)。

++*p:先将指针指向的内存地址中的值+1(*p=*p+1),再获取值(*p)

p++:先获取指针指向的内存地址(p),再指针指向下一个内存地址(p=p+1)。

++p:先指针指向下一个内存地址(p=p+1),再获取指向的内存地址(p)。

cs 复制代码
#include <stdio.h>

int main(void)
{
    int a[] = { 2024, 4, 23, 9, 20 };
    int *p;
    p = a;
    printf("p point to a,the first value is %d\n", *p++);      // *p++:获取值,再指向下一个
    printf("next value is %d\n", *p);
    return 0;
}

// 结果:
p point to a,the first value is 2024
next value is 4


#include <stdio.h>

int main(void)
{
    int a[] = { 2024, 4, 23, 9, 20 };
    int *p;
    p = a;
    printf("p point to a, next value is %d\n", *++p);      // *++p:指向下一个,再获取值
    printf("value is %d\n", *p);
    return 0;
}

// 结果:
p point to a, next value is 4
value is 4
cs 复制代码
#include <stdio.h>

int main(void)
{
    int a[] = { 2024, 4, 23, 9, 20 };
    int *p;
    p = a;
    printf("p point to a, value is %d\n", (*p)++);      // (*p)++:获取值,再值+1
    printf("value is %d\n", *p);
    return 0;
}

// 结果:
p point to a, value is 2024
value is 2025


#include <stdio.h>

int main(void)
{
    int a[] = { 2024, 4, 23, 9, 20 };
    int *p;
    p = a;
    printf("p point to a, value is %d\n", ++*p);      // ++*p:值+1,再获取值
    printf("value is %d\n", *p);
    return 0;
}

// 结果:
p point to a, value is 2025
value is 2025
cs 复制代码
#include <stdio.h>

int main(void)
{
    int a[] = { 2024, 4, 23, 9, 20 };
    int *p;
    p = a;
    printf("p point to a, address is %p\n", p++);      // p++:获取指向,再指向下一个
    printf("address is %p\n", p);
    return 0;
}

// 结果:
p point to a, address is 000000000061FE00
address is 000000000061FE04


#include <stdio.h>

int main(void)
{
    int a[] = { 2024, 4, 23, 9, 20 };
    int *p;
    p = a;
    printf("p point to a, address is %p\n", ++p);      // ++p:指向下一个,再获取指向
    printf("address is %p\n", p);
    return 0;
}

// 结果:
p point to a, address is 000000000061FE04
address is 000000000061FE04

5、指针数组

指针可以指向数组,而数组中的元素也可以是指针。若一个数组,该数组中每个元素都是指针,则称为指针数组。

cs 复制代码
int  (*a)[]         // 指向数组的指针,运算符优先级:"()"和"[]" > "*"
int  *a[]           // 数组指针,数组的元素是指针

注:字符串通常用数组表示。数组名就是内存地址(数组第一个元素的内存地址),字符串名也是内存地址(字符串第一个字符的内存地址)。占位符%p表示字符串的内存地址,%s表示字符串的内容。

cs 复制代码
#include <stdio.h>
#include <string.h>

int main(void)
{
    char *s[] = { "hello", "world", "good", "luck"};   // 数组中的元素都是指针,指向字符串 
    int n = sizeof(s) / sizeof(s[0]);                  // 获取数组元素个数

    for(int i = 0; i < n; i++)
    {
        // 输出数组中每个指针指向的内存地址和内容(字符串)
        printf("%d element is pointer, point to address %p, value is %s\n", i, s[i], s[i]);

        // 输出每个字符串中的每个字符
        for(int k = 0,m = strlen(s[i]); k < m; k++)
        {
            printf("%c ",s[i][k]);
        }
        printf("\n");
    }

    return 0;
}

// 结果:
0 element is pointer, point to address 0000000000404000, value is hello
h e l l o 
1 element is pointer, point to address 0000000000404006, value is world
w o r l d
2 element is pointer, point to address 000000000040400C, value is good
g o o d
3 element is pointer, point to address 0000000000404011, value is luck
l u c k

指针数组可以作为参数传入函数,则可以理解为传入函数的是一个指向指针数组的指针,可以通过指针的算术运算指向下一个或上一个元素。注意同时传入指针数组的元素个数,检查边界避免越界。

注:数组作为参数传入函数,视为传入函数的是指向数组的指针。

cs 复制代码
#include <stdio.h>

void printstring(char *[], int);                       // 指针数组作为函数参数

int main(void)
{
    char *s[] = { "hello", "world", "good", "luck"};   // 数组中的元素都是指针,指向字符串 
    int n = sizeof(s) / sizeof(s[0]);                  // 获取数组元素个数

    for(int i = 0; i < n; i++)
    {
        printf("%d element is pointer, point to address %p, value is %s\n", i, s[i], s[i]);
    }

    printstring(s, n);

    return 0;
}

void printstring(char *string[], int length)
{
    while(length-- > 0)
    {
        printf("pointer, point to  %s\n", *string++);
    }
}

// 结果:
0 element is pointer, point to address 0000000000404000, value is hello
1 element is pointer, point to address 0000000000404006, value is world
2 element is pointer, point to address 000000000040400C, value is good
3 element is pointer, point to address 0000000000404011, value is luck
pointer, point to  hello
pointer, point to  world
pointer, point to  good
pointer, point to  luck

main主函数可以接收命令行参数,argc是参数个数,argv是传入的所有命令行参数(第一个是文件名) 。argv is a pointer to an array of character strings that contain the arguments, one per string.

cs 复制代码
// pointer.c
#include <stdio.h>

int main(int argc, char *argv[])
{
    for(int i = 1; i < argc; i++)
    {
        printf("%d element is pointer, point to address %p, value is %s\n", i, argv[i], argv[i]);
    }

    printf("\n");
    
    while(--argc > 0)
    {
        printf("pointer, point to %s\n", *++argv);
    }

    return 0;
}

// TERMINAL 终端:
gcc -o pointer pointer.c       
./pointer hello world good luck

//  结果:
1 element is pointer, point to address 0000000000AA1430, value is hello
2 element is pointer, point to address 0000000000AA14A0, value is world
3 element is pointer, point to address 0000000000AA14C0, value is good
4 element is pointer, point to address 0000000000AA14E0, value is luck

pointer, point to hello
pointer, point to world
pointer, point to good
pointer, point to luck

注意区分: *++argv[0]、(*++argv)[0]

*++argv[0]:argv[0]的字符串中指向下一个字符,获取其值。

(*++argv)[0] :argv指向下一个参数,获取字符串的第一个字符。

【运算符优先级: () [ ] 高于 * ++】

cs 复制代码
// pointer1.c
#include <stdio.h>

int main(int argc, char *argv[])
{
    argv++;                                         // argv指向下一个元素,即argv[1]
    printf("*++argv[0] is %c\n", *++argv[0]);       // argv[1][0]下一个元素即argv[1][1]的值  
    return 0;
}

// TERMINAL 终端:
gcc -o pointer1 pointer1.c
./pointer1 hello world

// 结果:
*++argv[0] is e


// pointer2.c
#include <stdio.h>

int main(int argc, char *argv[])
{
    argv++;                                         // argv指向下一个元素,即argv[1]
    printf("(*++argv)[0] is %c\n", (*++argv)[0]);   // argv[1]下一个元素,即argv[2]的第一个字符
    return 0;
}

// TERMINAL 终端:
gcc -o pointer2 pointer2.c
./pointer2 hello world

// 结果:
(*++argv)[0] is w

指针数组类似于两维数组,但区别是:

指针数组:

  • 数组中的元素都是指针。
  • 指针指向的内存地址中的值可以占不同字节数。
  • 内存空间:所有指针所占内存空间+所有指针指向的内存地址中的值所占内存空间。

两维数组:

  • 数组中的元素是一维数组。
  • 数组中的元素占相同字节数。
  • 固定内存空间:所有元素所占内存空间。

6、函数指针

函数本身就是存储在内存里的一段代码,指针可以指向函数,通过指针调用函数。指向函数的指针称为函数指针。

  • 指向函数的指针,其参数、返回值和函数一致。
  • 必须用圆括号()将*和函数指针名括起来,即(*函数指针名)。若不加圆括号则表示函数返回指针。
  • 通过指针调用函数,和函数本身调用一样。
  • 函数指针指向函数,函数名就是函数的内存地址。
cs 复制代码
int (*p)(int, int)         // 函数指针p,指向带有2个int类型参数和返回int类型数值的函数
int *p(int, int)           // 函数p,有2个int类型参数,返回指向int类型的指针
cs 复制代码
#include <stdio.h>

// function prototype 函数原型
int sum(int, int);

int main(void)
{
    int x = 1, y = 2, z;

    int (*p)(int, int) = sum;      // 指针指向函数sum       
    z= p(1,2);                     // 通过指针调用函数
    printf("%d + %d = %d\n", x, y, z);

    return 0;
}

int sum(int a, int b)
{
    return a + b;
}

// 结果:
1 + 2 = 3

函数指针可以作为其它函数的参数,其它函数执行时通过指针调用的函数,称为回调函数。

cs 复制代码
#include <stdio.h>
#include <stdlib.h>

// function prototype 函数原型
int randdigit(void);
int sum(int (*)(void), int);        // 指针函数作为参数

int main(void)
{
    int y = 2, z;      
    z= sum(randdigit,2);            // 调用函数,其中一个参数是指针函数,将指向函数randdigit的指针传入函数
    printf("x + %d = %d\n", y, z);

    return 0;
}

// 产生随机数,并输出
int randdigit(void){
    int x = rand();
    printf("x = %d\n",x);
    return x;
}

// 求和,其中一个参数是函数randdigit返回的随机数
int sum(int (*a)(void), int b)     // 指针函数a作为参数,接收通过函数指针a获取的函数randdigit
{
    return a() + b;                // 通过函数指针a调用函数randdigit
}

// 结果:
x = 41
x + 2 = 43

补充:运算符优先级

类别 运算符 结合性
后缀 () [] -> . 从左到右
一元 + - ! ~ ++ - - (type)* & sizeof 从右到左
乘除 * / % 从左到右
加减 + - 从左到右
移位 << >> 从左到右
关系 < <= > >= 从左到右
相等 == != 从左到右
位与 AND & 从左到右
位异或 XOR ^ 从左到右
位或 OR | 从左到右
逻辑与 AND && 从左到右
逻辑或 OR || 从左到右
条件 ?: 从右到左
赋值 = += -= *= /= %= >>= <<= &= ^= |= 从右到左
逗号 , 从左到右
相关推荐
Dola_Pan1 分钟前
C语言:数组转换指针的时机
c语言·开发语言·算法
ExiFengs1 分钟前
实际项目Java1.8流处理, Optional常见用法
java·开发语言·spring
paj1234567893 分钟前
JDK1.8新增特性
java·开发语言
IT古董10 分钟前
【人工智能】Python在机器学习与人工智能中的应用
开发语言·人工智能·python·机器学习
繁依Fanyi14 分钟前
简易安卓句分器实现
java·服务器·开发语言·算法·eclipse
IU宝33 分钟前
C/C++内存管理
java·c语言·c++
湫ccc33 分钟前
《Python基础》之pip换国内镜像源
开发语言·python·pip
fhvyxyci34 分钟前
【C++之STL】摸清 string 的模拟实现(下)
开发语言·c++·string
qq_4597300337 分钟前
C 语言面向对象
c语言·开发语言
菜鸟学Python1 小时前
Python 数据分析核心库大全!
开发语言·python·数据挖掘·数据分析