Bootstrap

指针(3)

目录

初始化

char *p 的初始化

char **p 的初始化

总结

const注意点:

万能指针void* 的规则:

示例

使用 void* 的注意事项

结论

指针操作字符串数组

指针操作二维数组

回调函数:

优点

main 函数的定义

参数解释

示例代码



-----------------------------------------------------------------------------------------------------

初始化

char *p 的初始化

char *p 是一个指向字符的指针,通常用于指向一个字符数组(即字符串)或单个字符。你可以通过以下方式初始化 char *p

  1. 指向一个字符串字面量

    char *p = "Hello, world!";
    

    注意,字符串字面量是常量,存储在字符串常量区,不能通过 p 修改它的内容。

  2. 指向字符数组

    char arr[] = "Hello, world!";
    char *p = arr;  // `p` 指向字符数组 `arr` 的首元素

char **p 的初始化

char **p 是一个指向指针的指针,通常用于指向一个字符串数组或动态分配的二维字符数组。初始化 char **p 的方式有几种:

  1. 指向字符串数组

    char *array[] = {"Hello", "world", "!"}; // 字符串数组
    char **p = array;                       // `p` 指向 `array` 的首元素

总结

  • char *p 是指向字符的指针,可以指向字符数组、字符串字面量,或者通过动态分配内存来初始化。
  • char **p 是指向字符指针的指针,通常用于处理二维字符数组或字符串数组,可以通过指针数组初始化,也可以通过动态分配内存来初始化。


const注意点:


char*可以被赋给const char*,但是char**不能被赋给const char**

char**被赋给const char**时,会得到警告assignment from incompatible pointer type

也就是类型不兼容,这是因为char *和const char*的指向类型都为char,所以它们可以合法的赋值

但是char **与const char**的指向分别为char*与const char*,这两个显然不兼容,所以会得到警告

在 C 语言中,void* 类型被称作“万能指针”因为它可以指向任何类型的对象。使用 void* 的原因是 C 语言没有多态性,所以不能将基类指针安全地转换为派生类指针。void* 是一个通用的指针类型,允许存储任何类型对象的地址,但需要显式转换才能使用。

万能指针void* 的规则:

  • 不能直接操作 void* 指针:当你有一个 void* 指针时,你不能直接通过 * 运算符来访问它所指向的内存,因为它没有类型。

  • 需要类型转换:从 void* 类型转换为其他任何类型的指针时,都是合法的。但是,不能从任何有类型的指针转换到 void*

示例

#include <stdio.h>

int main() {
    int a = 10;
    void* ptr = &a;

    // 错误:不能直接解引用 void* 指针
    // int value = *ptr;

    // 必须进行类型转换后才可解引用
    int value = *((int*)ptr);
    
    printf("Value of a: %d\n", value);
    return 0;
}

在上面这个例子中,我们定义了一个 int 类型的变量 a,并创建了一个指向 a 的 void* 指针。我们想通过 ptr 来访问变量 a 的值,但是由于 ptr 是 void*,我们不能直接解引用它。相反,我们首先将 ptr 转换为 int* 类型,然后再解引用。

使用 void* 的注意事项

  • 内存对齐(Memory Alignment):当你使用 void* 指针时,许多操作系统和编译器会对内存进行对齐,这意味着你不能保证任意类型的指针与 void* 指针的地址是能够正确对齐的。直接读写 void* 指针指向的内存地址可能会导致未定义行为。

  • void* 无类型void* 指针不包含关于它所指向的数据类型的信息。也就是说,使用 void* 指针的时候,需要开发者来确保通过 void* 指针操作的对象的类型是正确的。

  • 内存泄漏风险:由于 void* 指针不具备类型信息,使用其进行内存管理(如 malloc()calloc()realloc()free())时,容易出现内存泄漏或者访问非法内存空间的情况。

结论

void* 类型作为一个通用的指针类型,经常用于风格各异的上下文中,比如内存管理或者回调函数的参数传递。然而,出于安全和清晰的考虑,应该尽量避免在自己的代码中使用它。当确实需要使用 void* 时,确保你已经理解了类型转换的语义和注意事项。

指针操作字符串数组

char s[10] = "hello";//存放字符串数据 
char *p = "hello";
    

char s[10]; //s的数据类型 char[10]

char[10] s1[3]; //二维数组 //此时的二维数组的元素  s1[3] 是一个一维字符型数组 
                //定义一个存放 字符串 的一维数组 


char *p = "hello"; //p的类型 char *  
                   //char *的指针变量p 相当于代表一个字符串 

                   
char *p1 = "hello";
char *p2 = "world";
char *p3 = "china";

char* pstr[3]= {"hello","world","china"}; //数组 --- 数组中存放是 各个字符串的地址
      //地址 ---存放这地址数据的数组 --- 指针的数组 ---指针数组 

在程序中

char* pstr[3]= {"hello","world","china"}

for(i=0;i<3;i++)
{
    printf("%s\n",pstr[i]);//  注意调用方式
}

指针操作二维数组

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

a 还是首元素的地址 a[0]、a[0][0]
 
  int[4] a[3];  //a数组名 -- &a[0] 

a<=>&a[0] //值的角度 
    a[0] //数据类型 int[4] 
    &a[0] //int[4] * 指针类型 
          //标准C语法: int (*)[4]

定义变量:
int (*p)[4]; //数组指针
             //基类型 int[4]这种类型 ---数组类型,与原来的int 基类型对比

*p //等价与 a[0] 
   //相当于是 int[4] 这个一维数组的数组名 

*(*(p+0)+0) <=> a[0][0]

*(*(p+i)+j) <=> a[i][j]      //从内到外,先确定行,再确定列

指针操作函数

int shortcmp(const void *a,const void *b)   //const修饰的形参变量则说明该形参只读不修改。

回调函数:

函数名 --- 代表函数的入口地址 

int add(int a,int b) // 函数  
                     // 函数名 - 代表函数的入口地址 
                     // 函数名对应的数据类型 
                     // int (int a,int b) //函数类型 
                     // 代表一类函数, 返回值为int型
                     //带有两个int型的形参变量 
                     
说明:
 1.可以定义一个函数类型的指针变量 
   来保存函数的入口地址 
 2.有了这个指针变量 
   通过指针变量 进行函数调用 

注意:这里调用函数  *p/*****p/p   都是一样的效果。

优点

  • 灵活性和可扩展性:只需更改传递的回调函数,即可改变行为,而不需要修改执行它的函数。
  • 代码解耦:调用者和被调用者之间没有紧密的耦合,允许独立修改。
  • 代码复用:相同的函数可以通过不同的回调函数达到不同的目的。

总之,回调函数是 C 语言中很有用的工具,使我们能够编写灵活且可维护的代码。了解和利用回调函数可以帮助你更好地处理各种编程任务,特别是在事件驱动和异步编程中。

42 int add(int a,int b)
 43 {
 44     return a+b;
 45 }
 46 
 47 int sub(int a,int b)
 48 {
 49     return a-b;
 50 }
 51 int mul(int a,int b)
 52 {
 53     return a*b;
 54 }
 55 int div(int a,int b)
 56 {
 57     return a/b;
 58 }
 59 void processData(int a,int b,int(*p)(int,int))//函数回调
 60 {
 61     printf("%d\n",p(a,b));
 62 }
 63 int main(int argc, const char *argv[])
 64 {
 65     int a[] = {9,8,7,6,5,4,3,2,1};
 66     int len = sizeof(a)/sizeof(a[0]);
 67 
 68 //  choieSortN(a,len,func1);
 69 
 70 //  printArray(a,len);
 71     processData(6,4,div);   
 72     return 0;
 73 }

main 函数的定义

main 函数的标准定义如下:

int main(int argc, char *argv[]) {
    // 程序代码
    return 0;
}

或者:

int main(int argc, char **argv) {
    // 程序代码
    return 0;
}

//int main(int argc, const char *argv[])
//argc  命令行参数的个数 
//argv  存放命令行参数的 字符串的指针数组 
int main(int argc, const char *argv[])
{

    printf("argc  = %d\n",argc);
    
    int i = 0;
    for (i = 0; i < argc;++i)
        printf("argv[%d]  = %s\n",i,argv[i]);

    
    return 0;
}

参数解释

  1. argc(Argument Count):

    • argc 是一个整数,表示传递给程序的命令行参数的数量。它包括程序的名称本身,所以最少值为 1。
  2. argv(Argument Vector):

    • argv 是一个指向字符指针数组的指针。每个 argv[i] 是一个指向命令行参数的字符串的指针。argv[0] 通常是程序的名称,argv[1]argv[2] 等是用户提供的其他参数。

示例代码

以下是一个简单的示例,演示如何使用 argc 和 argv

#include <stdio.h>

int main(int argc, char *argv[]) {
    printf("Number of arguments: %d\n", argc);

    printf("Program name: %s\n", argv[0]);

    for (int i = 1; i < argc; i++) {
        printf("Argument %d: %s\n", i, argv[i]);
    }

    return 0;
}

运行示例

假设编译后的程序名为 example,你可以用以下方式运行它,并传递参数

运行果将是:

Number of arguments: 4
Program name: ./example
Argument 1: arg1
Argument 2: arg2
Argument 3: arg3

通过了解和合理使用 argc 和 argv,你可以使你的程序更加灵活,能够处理多种输入情况

总结:

指针 + 二维数组 
 1.二维数组本质 ---一维数组  
 2.数组的特点 //连续性,有序性,单一性
 3.指针类型的定义 
 4.指针访问数组元素的 过程  
   *(*(p+i) + j)
 指针 + 指针数组 //
 指针 + 函数 
                 

;