掘根宝典之c语言字符指针,指针数组,数组指针,函数指针

目录

字符指针

字符指针指向字符串

使用字符指针

例子

指针数组

数组指针

数组名和&数组名 

数组名

sizeof(数组名),&数组名

&数组名

&数组名错误使用方法

数组名和&数组名用于一维数组

例子1

例子2

  数组名和&数组名用于二维数组

例子1

例子2

函数指针

通过函数指针调用函数


字符指针

在C语言中,字符指针是一种特殊的指针类型,用于指向字符数据的内存地址。

字符指针的声明形式为:

 char *字符指针名=字符串/字符串;

这种形式在c语言是允许的,但是c++禁止char*指针指向常量字符串

字符指针指向字符串

 char* str = "Hello,World!"; 

这意味着将字符串的首字符的地址赋给字符指针

我们可以验证一下

#include<stdio.h>
int main()
{
     char* str = "Hello,World!"; 

    printf("%p\n", str); 
    printf("%p\n", "Hello,World!"); 
    printf("%p\n", &str[0]);
    printf("%c\n", str[0]);

   
}

结果

事实证明确实如此

使用字符指针

使用字符指针时,有几个需要注意的地方:

  1. 字符指针的初始化:在定义字符指针时,可以直接赋值一个字符串常量,也可以通过动态内存分配来初始化指针。例如:

    char *str1 = "Hello World"; // 字符指针指向字符串常量
    char *str2 = malloc(20);    // 字符指针指向动态分配的内存
    

    注意,字符串常量是不可修改的,所以使用字符指针指向字符串常量时要谨慎,确保不会试图修改字符串。

  2. 字符指针的解引用:使用字符指针访问字符串中的字符时,可以使用指针自增运算符 ++ 来实现。例如:

    char *str = "Hello";
    while (*str) {
        printf("%c", *str++);
    }
    

    这里的 *str++ 先取出 str 指针指向的字符,然后将指针自增,指向下一个字符。

  3. 字符指针的比较:可以使用比较运算符 ==!=&lt;>&lt;=>= 来比较字符指针的大小。例如

    char *str1 = "apple";
    char *str2 = "banana";
    printf("%d", str1 < str2); // 输出1,表示str1在str2之前
    

    字符指针的比较是基于内存地址的,指向内存地址较低的字符指针比较小。

  4. 字符指针的修改:字符指针可以通过赋值来修改指向的字符串。但是,如果字符指针指向的是字符串常量,则不能修改指针指向的内容。例如:

    char *str = "Hello";
    str[0] = 'h'; // 错误,试图修改字符串常量
    

    如果需要修改字符串,可以使用字符数组来替代字符指针。

  5. 字符指针的空指针检查:在使用字符指针之前,建议先进行空指针检查。例如:

    char *str = NULL;
    if (str == NULL) {
        printf("Pointer is NULL\n");
    }
    

    避免在空指针上进行解引用操作,以防止程序崩溃或产生未定义行为。

例子

可以使用字符指针来访问字符串和字符数组,例如:

#include <stdio.h>

int main() {
    char *str = "Hello, World!";  // 字符串常量
    char arr[] = "Hello, World!"; // 字符数组

    printf("%s\n", str);  // 输出字符串
    printf("%c\n", *str); // 输出第一个字符

    printf("%s\n", arr);  // 输出字符数组
    printf("%c\n", arr[0]); // 输出第一个字符

    return 0;
}

结果

指针数组

在C语言中,指针数组是指一个数组,其每个元素都是指针类型的变量。

指针数组可以用于存储一组指针,每个指针可以指向不同的数据。

指针数组的声明方式为:

指针类型 *数组名[数组大小]

以下是一个指针数组的示例:

int main() {
    int num1 = 10, num2 = 20, num3 = 30;
    int *ptrArray[3]; // 声明一个包含3个int类型指针的数组

    ptrArray[0] = &num1; // 指针数组的第一个元素指向num1
    ptrArray[1] = &num2; // 指针数组的第二个元素指向num2
    ptrArray[2] = &num3; // 指针数组的第三个元素指向num3

    printf("%d\n", *ptrArray[0]); // 输出10,访问指针数组的第一个元素所指向的值
    printf("%d\n", *ptrArray[1]); // 输出20,访问指针数组的第二个元素所指向的值
    printf("%d\n", *ptrArray[2]); // 输出30,访问指针数组的第三个元素所指向的值

    return 0;
}

在上述示例中,声明了一个包含3个int类型指针的指针数组ptrArray。然后,通过将指针数组的元素指向各个变量,可以实现对这些变量的间接访问。通过解引用操作符 * 和数组下标,可以访问对应元素所指向的值。

指针数组在实际应用中有很多用途,比如可以用来存储字符串数组、函数指针数组等。

数组指针

在C语言中,数组指针是指一个指向数组的指针变量。

数组指针可以用来访问数组元素,也可以用来处理多维数组。

数组指针的声明方式为:

数据类型 (*指针变量名)[数组大小];

数组名和&数组名 

在C语言中,&数组名数组名的运算结果是不同的。

数组名

在C语言中,数组名是一个特殊的标识符,大多数情况下它表示数组的首元素的地址。

当使用数组名时,它会自动转换为指向数组第一个元素的指针。这意味着可以通过数组名来访问数组中的元素。

下面是一个示例代码来说明数组名的用法:

#include <stdio.h>

int main() {
    int numArray[5] = {1, 2, 3, 4, 5};

    printf("%p\n", numArray); // 输出数组的首地址
    printf("%p\n",&numArray[0]);
    return 0;
}

输出结果如下:

0x7fff5a40ce90
0x7fff5a40ce90

可以看到,numArray输出的是数组的首地址。

在一些情况下,可以将数组名用作指针来访问数组元素。例如,可以使用numArray[0]来访问数组的第一个元素,使用numArray[1]来访问数组的第二个元素,依此类推。

需要注意的是,数组名作为指针时是常量,不能对其进行赋值操作。也就是说,不能将数组名指向另一个数组或另一个地址。

sizeof(数组名),&数组名

绝大多数情况下,数组名代表数组首元素地址

但是有两种例外情况,sizeof(数组名)和&数组名,这里的数组名都代表整个数组

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

 printf("%d\n", sizeof(numArray)); 
 printf("%p\n", &numArray);// 输出整个数组的地址

 运行结果是

 

&数组名

&数组名:取得是整个数组的地址,也就是数组的首地址。

当使用&数组名时,它返回的是整个数组的指针,指向整个数组的存储空间

这与使用数组名(返回的是指向数组第一个元素的指针)有所不同。

&数组名一般用于二维指针

对于二维数组,&数组名表示整个二维数组的地址,二维数组的地址是数组首元素的地址,二维数组首元素是个数组。

所以&数组名,表示都是指向数组的指针

下面是一个示例代码来说明&数组名的用法:

#include <stdio.h>

int main() {
    int matrix[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};

    printf("%p\n", &matrix);      // 输出整个二维数组的地址
    printf("%p\n", matrix);       // 输出二维数组的首行地址,也即第一个子数组的地址
    printf("%p\n", &matrix[0]);   // 输出二维数组的首行地址,与上一行等效
    printf("%p\n", matrix[0]);    // 输出二维数组的首行地址,与上两行等效
    printf("%p\n", &matrix[0][0]); // 输出二维数组的第一个元素的地址

    return 0;
}

输出结果如下:

0x7fff5a40ce90
0x7fff5a40ce90
0x7fff5a40ce90
0x7fff5a40ce90
0x7fff5a40ce90

可以看到,&matrixmatrix&matrix[0]matrix[0]&matrix[0][0]都输出了相同的地址,即整个二维数组的地址。

需要注意的是,二维数组的地址类型是指向一维数组的指针,即int (*)[3]。这是因为在内存中,二维数组是按行存储的,每行是一个一维数组。

因此,对于二维数组,&数组名表示整个数组的地址,数组名表示首行的地址,&数组名[0]数组名[0]表示第一行的地址,&数组名[0][0]表示第一个元素的地址。

总结起来,&数组名表示获取整个数组的地址,返回的是指向整个数组的指针。

&数组名错误使用方法

我们看个例子

int a[5]={1,2,3,4,5};
int(*p)[5]=&a;
printf("%d",*p);//这是错误的

p是指向数组的,*p相当于数组名,数组名又相当于数组首元素首地址,以%d打印出来就是一堆乱码

数组名和&数组名用于一维数组

例子1

下面是一个示例代码来说明这两者的区别:

#include <stdio.h>

int main() {
    int numArray[5] = {1, 2, 3, 4, 5};

    printf("%p\n", &numArray); // 输出数组的首地址
    printf("%p\n", numArray); // 输出数组的首地址
    printf("%p\n", &numArray[0]); // 输出数组的首地址

    printf("%p\n", &numArray + 1); // 输出整个数组占用的内存空间后面的地址
    printf("%p\n", numArray + 1); // 输出数组第二个元素的地址
    printf("%p\n", &numArray[1]); // 输出数组第二个元素的地址

    return 0;
}

输出结果如下:

0x7fffc7f12680
0x7fffc7f12680
0x7fffc7f12680
0x7fffc7f12694
0x7fffc7f12684
0x7fffc7f12684

可以看到,&numArraynumArray&numArray[0]的值都是相同的,都是数组的首地址。而&numArray + 1 是整个数组占用的内存空间后面的地址,而numArray + 1&numArray[1]是数组第二个元素的地址。

需要注意的是,当数组作为函数参数传递时,实际上传递的是数组的指针(首地址),而不是整个数组。这是因为数组在参数传递过程中会自动退化为指针。因此,函数名&函数名的结果是相同的,都是函数的地址。

总结起来,&数组名表示整个数组的地址,而数组名表示数组的首元素的地址。在大多数情况下,使用数组名会自动退化为指向第一个元素的指针。

例子2

以下是一个数组指针的示例:

int main() {
    int numArray[5] = {1, 2, 3, 4, 5};
    int (*ptr)[5]; // 声明一个指向包含5个int类型元素的数组的指针变量

    ptr = &numArray; // 数组指针指向numArray数组的首地址

    printf("%d\n", (*ptr)[0]); // 输出1,访问数组指针指向的数组的第一个元素
    printf("%d\n", (*ptr)[1]); // 输出2,访问数组指针指向的数组的第二个元素
    printf("%d\n", (*ptr)[2]); // 输出3,访问数组指针指向的数组的第三个元素

    return 0;
}

在上述示例中,声明了一个包含5个int类型元素的数组numArray。然后,声明了一个指向包含5个int类型元素的数组的指针变量ptr,并将其指向numArray数组的首地址。

通过解引用操作符 * 和圆括号来定义操作符优先级,可以访问数组指针指向的数组的元素。在示例中,使用(*ptr)[index]的方式访问数组元素,其中index表示要访问的元素的下标

  数组名和&数组名用于二维数组

int a[2][4]={{1,2,3,4},{5,6,7,8}};
int (*b)[4]=a;

二维数组数组名代表它第一行的地址

 此时a代表二维数组首元素地址,但二维数组首元素是个数组啊,所以此时b指向了一个一维数组

我们再看下面这种情况

int a[2][4]={{1,2,3,4},{5,6,7,8}};
int (*c)[4]=&a;

此时&a代表整个二维数组的地址,也就是说b接受了一个二维数组的地址,但是b是一维数组指针,所以&a被隐式转换为a第一个子数组的地址

我们可以来验证一下

我们可以看到啊,b,c完全是一样的,均指向a[0]这个数组的地址。

a的类型是int[2][4],赋给b时存在隐式类型转换

&a代表整个二维数组的地址,是int(*)[2][4],这与c的int(*)[4]不同,所以赋给c时也存在隐式转换

事实上

下面这种情况也是一样的道理

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

int(*d)[2][4] = a;
int(*e)[2][4] = &a;

例子1

数组指针可以用于处理多维数组。例如,可以声明一个指向二维数组的指针来访问二维数组的元素。

int main() {
    int twoDimArray[2][3] = {{1, 2, 3}, {4, 5, 6}};
    int (*ptr)[3]; // 声明一个指向包含3个int类型元素的数组的指针变量

    ptr = twoDimArray; // 数组指针指向twoDimArray二维数组的首地址

    printf("%d\n", (*ptr)[0]); // 输出1,(*ptr)代表twoDimArray[0]
    printf("%d\n", (*ptr)[1]); // 输出2,访问数组指针指向的二维数组的第二个元素
    printf("%d\n", (*ptr)[2]); // 输出3,访问数组指针指向的二维数组的第三个元素

    return 0;
}

这里的*ptr代表*(ptr+0),即twoDimArray[0]

在上述示例中,声明了一个包含2行3列的二维数组twoDimArray。然后,声明了一个指向包含3个int类型元素的数组的指针变量ptr,并将其指向twoDimArray数组的首地址。通过(*ptr)[index]的方式访问二维数组的元素。

数组指针在C语言中是一个灵活而强大的工具,可以用来处理数组和多维数组的访问和操作。

例子2
#include<stdio.h>
int main()
{
  
    int twoDimArray[2][3] = { {1, 2, 3}, {4, 5, 6} };
    int(*ptr)[3]; // 声明一个指向包含3个int类型元素的数组的指针变量

    ptr = &twoDimArray; // 这里存在隐式转换

    printf("%d\n", (*ptr)[0]); // 输出1,访问数组指针指向的二维数组的第一个元素
    printf("%d\n", (*ptr)[1]); // 输出2,访问数组指针指向的二维数组的第二个元素
    printf("%d\n", (*ptr)[2]);//输出3
}

这里的*ptr代表*(ptr+0),即twoDimArray[0]

结果

1
2
3

和上面那个例子一样

函数指针

C语言函数指针是指向函数的指针变量。它可以用来存储函数的地址,并且可以通过函数指针调用函数。

对于函数而言,函数名和&函数名都是函数的地址

函数指针的声明格式为:

返回类型 (*指针变量名)(参数类型1, 参数类型2, ...);

以下是一个简单的例子:

#include <stdio.h>

// 函数声明
int add(int a, int b);

int main() {
    // 定义函数指针变量
    int (*ptr)(int, int);
    
    // 将函数的地址赋值给指针变量
    ptr = add;
    
    // 通过指针调用函数
    int result = ptr(3, 4);
    
    printf("Result: %d\n", result);
    
    return 0;
}

// 函数定义
int add(int a, int b) {
    return a + b;
}

在上面的例子中,我们声明了一个名为ptr的函数指针变量,类型为int (*)(int, int),即指向一个具有两个int类型参数并返回int类型的函数。然后,我们将add函数的地址赋值给ptr,并通过ptr调用add函数,将结果打印出来。输出结果为7

函数指针在C语言中常用于实现回调函数、函数式编程等高级技术。它可以动态地决定需要调用哪个函数,提高了程序的灵活性和可扩展性。

通过函数指针调用函数

在C语言中,可以通过函数指针调用函数。具体步骤如下:

  1. 声明函数指针:首先需要声明一个函数指针,指定函数的返回类型和参数类型。例如,要声明一个指向返回类型为int,参数类型为int的函数的指针,可以这样写:int (*ptr)(int);

  2. 赋值函数地址:将要调用的函数的地址赋值给函数指针变量。例如,如果有一个名为add的函数,可以将其地址赋值给ptrptr = add;

  3. 通过函数指针调用函数:通过函数指针变量调用函数,可以像调用普通函数一样使用函数指针。通过使用指针变量的名称(加上括号和参数),可以调用所指向的函数。例如,可以使用ptr(3)来调用函数add(3)

下面是一个简单的例子来说明如何通过函数指针调用函数:

#include <stdio.h>

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

int multiply(int a, int b) {
    return a * b;
}

int main() {
    int (*ptr)(int, int);  // 声明函数指针
    
    ptr = add;  // 将add函数的地址赋给指针变量
    int result = ptr(3, 4);  // 通过指针变量调用add函数
    printf("Result of add: %d\n", result);
    
    ptr = multiply;  // 将multiply函数的地址赋给指针变量
    result = ptr(3, 4);  // 通过指针变量调用multiply函数
    printf("Result of multiply: %d\n", result);
    
    return 0;
}

输出结果为:

Result of add: 7
Result of multiply: 12

在上面的例子中,我们声明了一个名为ptr的函数指针变量,类型为int (*)(int, int),即指向一个具有两个int类型参数并返回int类型的函数。然后,我们将add函数和multiply函数的地址分别赋值给ptr,并通过ptr调用这两个函数。最后,将结果打印出来。

  • 39
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值