栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

c语言指针的指针(深入理解c指针)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

c语言指针的指针(深入理解c指针)

目录

7.1 NDK简介

7.1.1 JNI步骤7.1.2 c/c++ 补充7.1.3 SDK 和NDK 区别:7.1.4 GCC编译C/C++的四个过程(面试)7.1.5 函数库分为静态库和动态库7.1.6 static的作用7.1.7 cmake 编译方式 7.2 C语言零散知识总结

7.2.1 标识符7.2.2 关键字7.2.3 数据类型7.2.4 字节大小7.2.5 数据类型转换7.2.6 字符串7.2.7 C语言作用域7.2.8 extern7.2.9 宏定义的使用

7.2.9.1 C预处 理器7.2.9.2 预定义宏7.2.9.3 预处理器运算符 7.2.10 typedef7.2.11 函数指针 7.3 指针(重要)

7.3.1 基本使用(指针初始化 地址打印)7.3.2 *p++ (*p)++ *++p ++*p 区别7.3.3 指针常量和常量指针7.3.3 野指针注意点7.3.4 通用指针 void *7.3.5 指针的算术运算(指针步长)7.3.6 指针与数组

7.3.6.1 指针与一维数组7.3.6.1 指针与二维数组 7.3.7 数组指针与指针数组(重要)

7.3.7.1 数组指针(行指针)7.3.7.2 指针数组(元素指针) 7.3.8 指针的指针7.3.9 函数指针7.3.10 举例 7.4 字符串7.5 结构体

7.1 NDK简介 7.1.1 JNI步骤
1.JNI步骤
目录: java/com.iauto.service
     service.java

(1)在service.java 中声明Native方法(即需要调用的本地方法)
(2)导出头文件(在java目录下执行)
javah  -classpath . -jni com.iauto.service.service
(3)编写so库
(4)编译Java源文件
在com.iauto.service目录下执行:
javac -d  类名.java 
(5)运行java文件,调用so库
java Main

查看签名(先需要生成的class):
javap -s -p com.iauto.service
注意点:最新版本的jdk没有javah 
7.1.2 c/c++ 补充

C++ 中调用 C的代码

//注意使用cmake编译时,.h头文件中需要加上
extern "C"  //是c++中调用c代码,同时也可以判断是c++ 调用还是c调用

#ifdef __cplusplus
extern "C" {
#endif               

//...

#ifdef __cplusplus
}
#endif 

一个类方式被多次调用(2中方式)

//方式1:
//Demo.h
#ifndef  _DEMO_H_
#define  _DEMO_H_
#endif 

//方式2:
#pragma once
7.1.3 SDK 和NDK 区别:

sdk (java)
NDK(c/c++) 提供.so

7.1.4 GCC编译C/C++的四个过程(面试)

预处理
gcc -E main.c -o main.i
编译阶段
gcc -S main.i -o main.s
汇编阶段
gcc -c main.s -o main.o
链接阶段
gcc main.o -o main.exe

预处理:
展开头文件,宏定义,条件编译处理
预处理阶段会把引用的头文件插入到我们本身的代码中,会把所有的宏定义替换掉,所有的注释取消,条件编译不符合的不进行编译。
编译:
编译器来检查代码的规范性,语法错误等,检查无误后编译器会把代码编译成汇编代码
汇编:
汇编阶段把.s 文件翻译成二进制指令文件
链接
链接函数库,生成可执行文件

7.1.5 函数库分为静态库和动态库

静态库
这类库的名字一般是libxxx.a;
静态函数库编译成的文件比较大,因为整个函数库的所有数据都会被整合进目标代码中,他的优点就显而易见了,即编译后的执行程序不需要外部的函数库支持.当然这也会成为他的缺点,因为如果静态函数库改变了,那么你的程序必须重新编译。

动态库
这类库的名字一般是libxxx.so;相对于静态函数库,动态函数库在编译的时候 并没有被编译进目标代码中,你的程序执行到相关函数时才调用该函数库里的相应函数,因此动态函数库所产生的可执行文件比较小。由于函数库没有被整合进你的程序,而是程序运行时动态的申请并调用,所以程序的运行环境中必须提供相应的库。动态函数库的改变并不影响你的程序,所以动态函数库的升级比较方便.

//静态库:libxxx.a
编译的过程中进行链接
编译好的可执行文件size较大
移植性好,移植性可执行文件时 不需要移植如库文件
当库函数或者库文件升级后,需要重新编译

//动态库:libxxx.so
运行时进行链接
编译好的可执行文件size较小
移植性差/移植性可执行文件时,需要移植如库文件
当库函数或者库文件升级后,不需要重新编译

//创建静态库
1. 新建文件 hello.c test.c
2. 编译成目标文件 gcc -c hello.c -o hello.o
3. 编译成库 ar crs libmyHello.a hello.o
4. 链接库 gcc test.c -lmyHello -L.

//创建动态库
1. 新建文件 hello.c test.c
2. 编译成目标文件 gcc -fPIC -Wall -c hello.c -o hello.o
3. 编译成动态库 gcc -shared hello.o -o libmyHello.so
4. 链接库 gcc test.c ./libmyHello.so

查看链接的动态库:ldd

windows 平台
1 .dll:动态链接,作为共享函数库的可执行文件
2 .obj:目标文件,相当于源代码对应的二进制文件,为经过重定义
3 .lib:可理解为多个 obj 的集合,本质与 .obj 相同
linux 平台
1 .so :(shared ojbect)动态链接库,和windows上 dll类似
2 .o:目标文件
3 .a:与.o类似,多个.o 的集合,类似windows 下lib
Android如何配置cmakelist.txt 配置编译动态库和静态库呢

add_library(jinInterface SHARED library.c library.h)// SHARED 表示是动态库
add_library(jinInterface STATIC library.c library.h)// STATIC 表示是静态库
ADD_LIBRARY(...)
#语法:ADD_LIBRARY(libname [SHARED|STATIC] )

#上面的表达式等同于:
set(LIB_SRC library.c library.h)
add_library(jinInterface SHARED ${LIB_SRC})
7.1.6 static的作用
C中static的作用(具有隐藏效果)
1.static修饰全局变量时,该全局变量只能在本文件内使用,全局变量默认为0
2.static修饰局部变量时,如果该局部变量没有被初始化,其值为0,如果已经初始化过,则只会被初始化一次,下一次的值依据前一次的结果
3.static修饰函数时,该函数只能在本文件内使用

C++中static作用:
1.类的静态成员函数是属于整个类而非类的对象,所以它没有this指针,这就导致 了它仅能访问类的静态数据和静态成员函数。
2.不能将静态成员函数定义为虚函数。  
3.由于静态成员函数没有this指针,所以就差不多等同于nonmember函数,结果就 产生了一个意想不到的好处:成为一个callback函数,使得我们得以将C++和C-based X W indow系统结合,同时也成功的应用于线程函数身上。
4.静态成员初始化与一般数据成员初始化不同
7.1.7 cmake 编译方式
# windows下cmake编译方式
windows + vscode(gcc/g++) + cmake
配置:https://blog.csdn.net/weixin_41477306/article/details/108035004

编译方式1:
自动编译
每次修改最外层的CMakeLists.txt 文件都会自动进行编译

编译方式2:
手动编译
最外层目录下
mkdir build
cd build
cmake -G "MinGW Makefiles"  ..;mingw32-make

windows设置utf8方式,打开终端
chcp 65001
7.2 C语言零散知识总结 7.2.1 标识符

字母,数字,下划线组成,开头必须是字母或者下划线

7.2.2 关键字
auto   		//限定只能在函数里面使用
register 	//声明寄存器变量,不在内存中使用 
void   		//声明函数无返回值或无参数,声明无类型指针
extern  	//表示全局变量,对程序内所有文件可见,类似与java中的public
volatile 	//说明变量在程序执行中可被隐含地改变
static  	//表示全局的默认存储类,表示变量在程序生命周期内可见
double 
typedef 
short char const static int struct unsigned long union signed  float enum 
if else
switch case
for do while goto continue break default sizeof return
7.2.3 数据类型
说明字符型短整型整形长整型单精度浮点型双精度浮点型
数据类型charshortintlongfloatdouble
长度124448

构造类型 数组 结构体 共用体 枚举 指针 void

7.2.4 字节大小



注意:
32 指针占4 字节 64 位指针占8字节
32为long4字节 64我long8字节

7.2.5 数据类型转换

7.2.6 字符串

字符类型由单引号’ '包围,字符串由双引号" "包围。
const 和 define
1.定义:const常量有数据类型,宏常量没有数据类型,直接进行替换
2.检查const修饰的数据类型进行安全检查,define没有类型安全检查
3.编译器:有些编译器可以对const常量进行调试,但不能对宏进行调试
4.作用域不同 define只要被定义在本文件中有效,const只在定义的函数内有效

    printf("---------------字符串打印------------------n");
    char *str = "hello"; //静态常量去
    char  str1[] = "hello"; //在栈中
    if(str ==  str1) {
        printf("相等[%x] [%x]n",str,str1);
    }else{
       printf("不相等[%x] [%x]n",str,str1);
    }

    char *str2 = "hello";
    if(str2 ==  str) {
        printf("相等 [%x] [%x]n",str,str2);
    }else{
       printf("不相等[%x] [%x]n",str,str2);
    }

不相等[4040c9] [61fdba]
相等 [4040c9] [4040c9]
7.2.7 C语言作用域

1.在函数内部的局部变量
2.在所有函数外部的全局变量
3.在形式参数的函数参数定义中
当局部变量被定义时,系统不会对其初始化,必须要程序员对其初始化,定义全局变量时,系统自动会为其初始化。

7.2.8 extern
extern.c
#include
int extern_i = 300; //定义外部变量extern_i 
void extern_fun(int a,int  b) //定义外部函数
{
  printf("extern_fun [%d] [%d]n",a,b);
}

auto.h
#include
extern int extern_i;//extern  定义表示调用外部变量
extern void extern_fun(int a,int  b);
int main() {
    auto a = 100;
    printf("auto a = %d",sizeof(a));
    printf("extern_i = %d",extern_i);
    return 0;
}
7.2.9 宏定义的使用 7.2.9.1 C预处 理器

C 预处理器不是编译器的组成部分,但是它是编译过程中一个单独的步骤。简言之,C 预处理器只不过是一个文本替换工具而已,它们会指示编译器在实际编译之前完成所需的预处理。我们将把 C 预处理器(C Preprocessor)简写为 CPP。所有的预处理器命令都是以井号(#)开头。它必须是第一个非空字符,为了增强可读性,预处理器指令应从第一列开始。下面列出了所有重要的预处理器指令:

指令描述
#define定义宏
#include包含一个源代码文件
#undef取消已定义的宏
#ifdef如果已经宏已经定义了,则返回真
#ifndef如果宏没有定义,则返回真
#if如果给定条件为真,则编译下面带
#else#if替代方案
#elif如果前面的#if给定条件不为真,当前条件为真,则编译下面代码
#endif结束一个#if…#else条件编译块
#error当遇到标准错误时,输出错误消息
#pragma使用标准化方法,向编译器发布特殊的命令到编译器中
7.2.9.2 预定义宏

ANSI定义了许多宏,在编程中可以直接来使用这些宏。但是不能直接修改这些预定义宏。

指令描述
DATE当前日期,一个以"MMMM DD YYYY"格式表示的字符常量
TIME当前的时间,一个以"HH:MM:SS"格式表示的字符常量
FILE这会包含当前文件名,一个字符串常量
LINE这会包含当前行号,一个十进制常量
STDC当编译器以ANSI标准编译时,则定义为1

使用:printf("%s %d",FILE,LINE)

7.2.9.3 预处理器运算符

C 预处理器提供了下列的运算符来帮助您创建宏:
(1)宏延续运算符()
一个宏通常写在一个单行上。但是如果宏太长,一个单行容纳不下,则使用宏延续运算符()。
(2)把参数转换成字符串(#)
把参数转换成字符串

//例1
#define PF_T(a,b) 
            printf(#a " == hello world== " #b"n");
PF_T(你好,100);
输出: 你好 == hello world== 100

//例2 #define P(A) printf("%s:%dn",#A,A);
int a - 1;
P(a);P(b);

(3)合并两个变量成一个新的变量(##)将带参数的宏定义中将两个子串(token)联接起来,从而形成一个新的子串;但它不可以是第一个或者最后一个子串。所谓的子串(token)就是指编译器能够识别的最小语法单元.

//例1:
#define PF_ARG(a) 
            printf("token" #a "= %d n",token##a)
    int token32 = 200;
    PF_ARG(32);
//输出:
token32= 200

//注意:宏定义是直接替换
#define SUB(n,m) n*m //改变后 SUB(n,m) (n)*(m)
    int r = SUB(1+1,2+2);// 1+1*2+2 r = 5

(4) 可变参数的宏 __VA_ARGS__

#define LOG(...) printf(__VA_ARGS__);
//使用
LOG("score is %dn",96);

(5)##__VA_ARGS__

//宏前面加上##的作用在于,当可变参数的个数为0时,这里的##起到把前面多余的","去掉的作用,否则会编译出错
//举例1:
#define my_print2(fmt,...)  printf(fmt,__VA_ARGS__)  
//使用1. my_print1("i=%d,j=%dn",i,j) 正确打印
//使用2 my_print2("iiiiiiin")       编译失败打印,因为扩展出来只有一个参数,至少要两个及以上参数
举例2:
#define my_print2(fmt,...)  printf(fmt,##__VA_ARGS__)  
那么 my_print1里面不管是几个参数都能正确打印
7.2.10 typedef
//C 语言提供了 typedef 关键字,您可以使用它来为类型取一个新的名字。下面的实例为单字节数字定义了一个术语 BYTE:
typedef unsigned char BYTE;
//在这个类型定义之后,标识符 BYTE 可作为类型 unsigned char 的缩写,例如:
BYTE  b1, b2;
7.2.11 函数指针

函数指针基本用法:(主要用在回调上)

//⽅法⼀:
//声明⼀个函数类型
typedef void  (myTypeFunc)(int a, int b);
//定义⼀个函数指针
myTypeFunc    *myfuncp = NULL;     //定义⼀个函数指针 这个指针指向函数的⼊⼝地址

//⽅法⼆:(JNI中大部分使用这种方式)
//声明⼀个函数指针类型
typedef void (*myPTypeFunc)(int a, int b);          
//定义⼀个函数指针
myPTypeFunc   fp = NULL;           //通过 函数指针类型  定义了 ⼀个函数指针

函数指针在C++中使用,
typedef void (*handCallback)(int option,void *msg);
void REQ(handCallback call);
    class CallBack 
    {
        public:
            static void REQCallback(int option,void *msg){}
    };

int main()
{
    REQ(nutshell::CallBack::REQCallback);
}
7.3 指针(重要)

指针是一个变量,其值为地址,必须要为其初始化
声明指针或者不在使用后都要将其置为0 (NULL)
野指针 未初始化的指针
悬空指针 指针最初指向的内存已经被释放的一种指针
指针在32位机器上 占4个字节 64位占8个字节

7.3.1 基本使用(指针初始化 地址打印)

&号 用来取一个对象的地址
*号 一个间接寻址符,用于访问指针所指向的地址的值。

    int *p;
    int a = 200;
    p = &a; //指针初始化
    *p = 100;
    printf("a = %d n",a);
    printf("*p = %dn",*p);
    a=300;    
    printf("a = %d n",a);
    printf("*p = %dn",*p);
    printf("p的地址:[%x]  a的地址[%x]n",p,&a); //p的地址:[61fdf4]  a的地址[61fdf4]
    //两个地址是相同的,因为都指向的是同一片空间,一个值发生改变,另一个值也变
//输出:
a=100
*p=100
a=300
*p=300
7.3.2 *p++ (*p)++ *++p ++*p 区别
//例如:
int attr[] = {3,10,15,20,25,40};
int *p1= attr;
int *p2= attr;
int *p3= attr;
int *p4= attr; 
//p1 p2 p3 p4 的地址都指向attr[0]的初始地址,不管p1-p4那一个改变了attr数组的值,其他的都变
    
//printf 计算从右向左
printf("*p1++   : %d   %dn",*p1++,*p1);  /
//例1:
    int a = 100;
    int *p = &a;
    printf("p   [%p]n",p);
    printf("p+1 [%p]n",p+1);
    printf("p+2 [%p]n",p+2);
    printf("p+3 [%p]n",p+3);

    char c = 'a';
    char *pc = &c;
    printf("pc   [%p]n",pc);
    printf("pc+1 [%p]n",pc+1);
    printf("pc+2 [%p]n",pc+2);
    printf("pc+3 [%p]n",pc+3);

    int v = 100;
    void *vv =(void *) &v;     
    printf("vv   [%p]n",vv); //void类型 步长为1
    printf("vv+1 [%p]n",vv+1);
    printf("vv+2 [%p]n",vv+2);
    printf("vv+3 [%p]n",vv+3);

输出:指针64占8字节
p的步长为4 int 型
pc的步长为1 char 型
p  [000000000061FE14]
p+1 [000000000061FE18]
p+2 [000000000061FE1C]
p+3 [000000000061FE20]

pc   [000000000061FE0B]
pc+1 [000000000061FE0C]
pc+2 [000000000061FE0D]
pc+3 [000000000061FE0E]

vv   [000000000061FDFC]
vv+1 [000000000061FDFD]
vv+2 [000000000061FDFE]
vv+3 [000000000061FDFF]
7.3.6 指针与数组 7.3.6.1 指针与一维数组

int a[10];
a &a a+1 &a+1
a :代表数组首个元素的地址 = &a[0]
a+1 :代表数组首个元素的地址+1,即为&a[1]的 地址

&a :代表的是数组首地址
&a+1:代表数组首地址+1,即&a[0] 的地址 + sizeof(int)*10,是跳过整个数组的地址

注意:只有使用“&数组名”时,才是取数组首地址;数组名或者&数组名[0]都是取得数组首元素地址,另外,首地址+1得到的是跳过整个数组的地址,首元素地址+1得到的是下一个元素的地址。

1.基本使用

//例1;    
int array[] = {1,2,3,4,5,6}; //数组名就是数组的起始地址,数组名就是一个常量指针
int *ptr_array = array;
    
//打印地址
printf("address: [%x] [%x] [%x] [%x] n",array+3,&array[3],ptr_array+3,&ptr_array[3]);   //代表同一个地址
//打印值
ptr_array[1] = 100;
printf("value: [%d] [%d] [%d] [%d] [%d] [%d]n",array[1],*(array+1),ptr_array[1],*(ptr_array+1),*&array[1],*&ptr_array[1]);
//代表同一个值

//地址打印
printf("array[%x] &array+1 [%x] &ptr_array+1 [%x] n",array,&array+1,ptr_array+1);
//array:首地址  &array+1:取首地址+1:+1是加整个数组+24      ptr_array+1 :表示首地址+四个字节

//输出:
address: [61fdfc] [61fdfc] [61fdfc] [61fdfc]
value: [100] [100] [100] [100] [100] [100]
array[61fdf0] array+1[61fdf4] &array+1 [61fe08] &ptr_array+1 [61fdf4]



//例2:
int array[]={1,2,3,4,5,6};
int *ptr_1= (int *)(&array+1);
printf("array[%x] array+1[%x] *(array+1)[%d] n &array[%x] &array+1[%x] *(&array+1)[%d]n ptr_1[%x] ptr_1-1[%x] *(ptr_1-1)[%d]n",array,array+1,*(array+1),&array,&array+1,*(&array+1),ptr_1,ptr_1-1,*(ptr_1-1));
//array    :数组的首个元素的地址
//array+1  :数组首个元素地址+1 array[1] 的地址
//&array   :数组首元素地址
//&array+1 :数组首元素地址+1,地址:array+array*sizeof(int)*6
//ptr_1    :数组的首元素+1的地址
//ptr-1 -1 :数组的首元素+1的地址 - sizeof(int)  
打印:
array[61fd70] array+1[61fd74] *(array+1)[2]
&array[61fd70] &array+1[61fd88] *(&array+1)[6421896]
ptr_1[61fd88] ptr_1-1[61fd84] *(ptr_1-1)[6]

2.一维数组函数传参
一般的函数传参分为 传值 和 传址,传值 意味着对实参没有影响,仅仅是对拷贝进行操作;传址 意味着可以访问实参指针所指向的值,也可以对其进行操作。
数组传参比较有趣的地方在于既是 传址 也是 传值。传址是传递数组的地址或首元素的地址,可以通过该指针对数组进行间接访问,也修改数组值;传值是因为,对形参指针的操作并不影响实参指针。
2种方式:
void pointOneArg1(int *attr)
void pointOneArg2(int attr[])

void pointOneArg1(int *attr) {
    printf("pointoneArg [%d]n",sizeof(attr));
    *attr = 100;
}
void pointOneArg2(int attr[]) {
    printf("pointOneArg1 [%d]n",sizeof(attr));
    attr[5]=111;
}

int ptr_arg[10] = {1,2,3,4,5,6,7,8,9};
for (size_t i = 0; i < sizeof(ptr_arg)/sizeof(ptr_arg[0]); i++){
    printf("[%d] ",ptr_arg[i]);
}
printf("n");
pointOneArg1(ptr_arg);
for (size_t i = 0; i < sizeof(ptr_arg)/sizeof(ptr_arg[0]); i++){
    printf("[%d] ",ptr_arg[i]);
}
printf("n");
pointOneArg2(ptr_arg);
for (size_t i = 0; i < sizeof(ptr_arg)/sizeof(ptr_arg[0]); i++){
    printf("[%d] ",ptr_arg[i]);
}
printf("n");

//打印:
[1] [2] [3] [4] [5] [6] [7] [8] [9] [0]
pointoneArg [8]
[100] [2] [3] [4] [5] [6] [7] [8] [9] [0]
pointOneArg1 [8]
[100] [2] [3] [4] [5] [111] [7] [8] [9] [0]
7.3.6.1 指针与二维数组

int a[6][10];
1:访问地址
a :第一行元素的地址
a+n:表示第n+1行元素地址

*a和a[0] :表示第一行第一个元素的地址,&a[0][0]表示第一行第一个元素的地址,因此a=&a[0]=&(&a[0][0]),a表示地址的地址
*(a+n)+n和a[n]+n: 表示第n+1行第n+1个元素地址
2:访问元素
a[n][n]和 *(*(a+n)+n) :访问第n+1行第n+1个元素,可以用

、、练习:
int array2[][3]={1,2,3,4,5,6,7,8,9};
//array2      :行地址 array2+1 表示第1行的地址
//array2[0]   :*array2 表示第0行第0个元素的地址
//array2[0]+1 :*(array2)+1 表示0行第1个元素的地址
//array2[1][1]:*(*(array2+1)+1) 1行1列的值
printf("[%x]  [%x]n",array2,array2+1);      //表示0行地址 1行地址
printf("[%x]  [%x]n",array2[0],array2[1]);  //0行0列地址 1行0列地址
printf("[%x] [%x]n",array2[0]+1,*(array2+1)+1);//同上
printf("[%d] [%d]n",array2[1][1],*(*(array2+1)+1));
//输出:
[61fdd0]  [61fddc]
[61fdd0]  [61fddc]
[61fdd4] [61fde0]
[5] [5]

3 二维数组进行函数传参
二维数组int a[2][3]中 a 是一个指向整型数组的指针,又名数组指针。
int a[2][2];
int(*p)[2] = a; 数组指针:是一个指针,指针指向的int型的数组

多维数组进行函数传参时,下面两种形式任选一种即可
void func(int (*p)[2]); //存的是行指针
void func(int p[][2]);

//练习:
   int array_2[][3]={{1,2,3},{11,22,33},{111,222,333}};
   //地址打印
   printf("行地址 [%x] [%x]n",array_2,array_2+1);//表示行地址
   printf("行的0元素地址 [%x] [%x]n",array_2[0],array_2[1]);
   printf("行的0 ,1元素地址 [%x] [%x]n",*(array_2),*(array_2+1));
   printf("1行1个元素地址 [%x]n",*(array_2+1)+1);

//打印:
行地址 [61fd80] [61fd8c]
行的0元素地址 [61fd80] [61fd8c]
行的0 ,1元素地址 [61fd80] [61fd8c]
1行1个元素地址 [61fd90]
7.3.7 数组指针与指针数组(重要)

数组指针:(指的是行指针)
int (*p)[5] 这里的p是一个指针,指向一个具有5个元素的数组指针

指针数组:(指的是元素指针)
int *p[5] 这里的p是一个数组,数组中的元素类型是int*

运算符优先级: () > [] > *
指针数组:表示的是一个数组,数组中每一个变量都是指针型变量。
数组指针:表示的是一个指针类型的变量,这个指针变量指向的是一个数组。
例如:
int (*p1)[10];它是1个数组指针,这个指针变量指向一个数组.
数组的列数为10,增量为*(p1+i) 表示的是p[i][0]的地址
*(p+2)+3表示a[2][3]地址(第一行为0行,第一列为0列),*(*(p+2)+3)表示a[2][3]的值

7.3.7.1 数组指针(行指针)

数组指针练习
数组指针本身就是二维指针,表示的是行指针, 指向一维二维数组的行指针。
数组指针指向一维时:
int a[]={1,2,3};
int (*ptr)[3] = &a; //行指针地址
数组指针指向二维时 :
int a[][3] ={1,2,3,4,5,6,7,8,9,10};
int (*ptr)[3];
ptr=a; //行指针地址

//数组指针 与一维数组
int array_1[]= {1,2,3,4,5,6,7,8,9,10};
int (*ptr_1)[10]=&array_1;          //必须要取地址,取的是首地址
printf("[%d] [%d] [%d] [%d] [%d]n",ptr_1[0][0],ptr_1[0][1],ptr_1[0][2],ptr_1[0][3],ptr_1[0][4]);

//数组指针与二维数组
int array1[][3]={1,2,3,4,5,6,7,8,9};//定义 x 行 3 列的数组
int (*ptr)[3]=array1;
//打印值
printf("%d %d %d %dn",array1[0][0],ptr[0][0],*(*(array1+1)+1),*(*(ptr+1)+1));//打印0行0列  1行1列的值
ptr++;//行地址+1
printf(" %d n",*(*(ptr)));//表示1行0列的值  4

//输出:
[1] [2] [3] [4] [5]
1 1 5 5
4
7.3.7.2 指针数组(元素指针)

int *p[10] ; 它是一个指针数组,数组中的每一个变量都是指针型变量,
表示定义了p[0] p[1] … p[9] 个指针变量
指针数组与一维数组
int array2[3] = {12,3};
int *ptr[2];
ptr[0]=array2;

指针数组与二维数组
int *ptr_22[3];
int array22[][2] ={1,2,3,4,5,6};
for (size_t i = 0; i < sizeof(array22)/sizeof(array22[0]); i++)
ptr_22[i] = array22[i];//将i行第0个元素的地址赋给指针数组

//指针数组 字符串练习
    char *str11[] = {"a","bb","cc"};//表示它是一个数组,数组内的每个元素是指针
    for (size_t i = 0; i < 3; i++)
    {
        puts(str11[i]);
        printf("[%d]  [%x]n",i,str11[i]);
    }
//输出:
a
[0]  [404139]
bb
[1]  [40413b]
cc
[2]  [40413e]

指针数组与二维数组

    //指针数组与一维数组(指针数组指向一维数组的,元素指针)
    int array2[] = {1,2,3,4,5,6,7};
    int *ptr_2[2];
    ptr_2[0] = array2; //首行第一个元素的地址
    printf("%d %dn",ptr_2[0][0],ptr_2[0][1]);
    
//指针数组与二维数组(指针数组 指向二维数组的元素指针)
    int *ptr_22[3];
    int array22[][2] ={1,2,3,4,5,6};
    for (size_t i = 0; i < sizeof(array22)/sizeof(array22[0]); i++){
        ptr_22[i] = array22[i];//将i行第0个元素的地址赋给指针数组
    }
    
    for (size_t i = 0; i < 3; i++)
    {
        for (size_t j = 0; j < 2; j++)
        {
            printf("[%d]",ptr_22[i][j]);
        }
    }

//输出:
1 2
[1][2][3][4][5][6]
7.3.8 指针的指针
void fun(char *str,int *indx,char c,char **strr) {//strr n会把修改后的值带回
    int i=0;
    while (*str!=NULL)
    {
        if (*str == c)
        {
            *strr = str;
            *indx = i;
            break;
        }
        i++;
        str++;
    }
}
    int a = 100;
    int *ptr = &a;
    int **ptrr = &ptr;
    printf("[%d] [%d] [%d]n",a,*ptr,**ptrr);


    char str[] = "hanbaf baop bmkand aob";
    char *strr = NULL;
    int indx;
    fun(str,&indx,'k',&strr);//找到下标以k开头,在把k之后的打印出来
    printf("[%d] [%s]n",indx,strr);

输出:
[100] [100] [100]
[14] [kand aob]
7.3.9 函数指针

在C++中定义函数指针时,回调函数必须为静态
主要用来函数回调

//c++中使用:
#ifndef  _MODULE_H_
#define  _MODULE_H_
#include
#include
namespace iauto
{
#define REJ(name) (name)
typedef struct {
    int (*Ptr)(int option);
}Option;

class Module
{
public:
    static int Init(int option);
    static int OnCreate(int option);
    static int OnStart(int option);
};    
}
#endif

#include "Module.h"
namespace iauto
{
static std::string TAG = "Module:";    
int Module::Init(int option)
{
    std::cout<
typedef void (*Func)(int,int);
void add(int a,int b)
{
    printf("add a %d b %dn",a,b);
}

void sub(int a,int b)
{
    printf("sub a %d b %dn",a,b);
}

void funTest(Func f,int a,int b)
{
    f(a,b);
}
int main()
{
    funTest(add,1,2);
    funTest(sub,10,20);
}


//举例(2)
//c中结构体和函数指针的使用
BTE->HCI->HCI(int)->HCI(setCallback)
                        <--callback
-----------------------------
bet.h

#ifndef _BTE_H_
#define _BTE_H_

typedef void (*INIT)();
typedef void (*OPTION)(int);
typedef struct
{
    INIT Init_T;
    OPTION Option_T;
}BTE;

extern  const BTE * getBTE();
#endif
-----------------------------------
betcallback.h

#ifndef _BTECALLBACK_H_
#define _BTECALLBACK_H_
typedef void (*Callback)(int);
typedef struct
{
   Callback cB;
}BteCallback;
extern const BteCallback * getBteCallback();
#endif
-----------------------------------
hci.h

#ifndef _HCI_H_
#define _HCI_H_
#include"bteCallback.h"
typedef  void *  TRANSMIT;
typedef void (*init_T)();
typedef void (*SetCallback)(BteCallback *);
typedef void (*transmit_T)(TRANSMIT trans);
typedef struct
{
    int size;
    init_T init;
    SetCallback setcallback;
    transmit_T transmit;
}bt_hc_interface_t;
extern const bt_hc_interface_t * getbluetoothHCIInterace();
#endif
---------------------------------------
utildef.h

#ifndef _UTILDEF_H_
#define _UTILDEF_H_
#include
#define LOG_DBG(...)    printf(__VA_ARGS__);fflush(stdout)
typedef struct
{
    int a;
    int b;
    char c;
}MESSAGE_T;
#endif

----------------------------------------------
bte.c

#include"bte.h"
#include"hci.h"
#include"UtilDef.h"
static bt_hc_interface_t * hci = NULL;
static void init()
{
    LOG_DBG("BTE [%s]n",__FUNCTION__);
    hci = (bt_hc_interface_t *)getbluetoothHCIInterace();
    hci->init();
    MESSAGE_T * message = (MESSAGE_T *)malloc(sizeof(MESSAGE_T));
    message->a = 10;
    message->b = 110;
    message->c = 'A';
    hci->transmit((TRANSMIT)message);
}
static void option(int op)
{
    LOG_DBG("BTE %s [%d]n",__FUNCTION__,op);
    switch (op)
    {
    case 0:
    {
        if(hci!=NULL) {
            hci->setcallback(getBteCallback());
        }
    }
    break;
    default:
        break;
    }
}
static BTE bte = {
    init,
    option,
};

const BTE * getBTE()
{
    LOG_DBG("BTE %sn",__FUNCTION__);
    return &bte;
}
---------------------------------
bteCallback.c

#include"bteCallback.h"
#include"UtilDef.h"
static void Call(int value)
{
    LOG_DBG("BTECALLBACK [%s] [%d]",__FUNCTION__,value);
}
static BteCallback callback = {
    Call
};
const BteCallback * getBteCallback()
{
    LOG_DBG("BTECALLBACK [%s]",__FUNCTION__);
    return &callback;
}
---------------------------------
hci.c


#include"hci.h"
#include"bteCallback.h"
#include"UtilDef.h"
static BteCallback *callback;
static void init()
{
    LOG_DBG("HCI [%s]n",__FUNCTION__);
}
static void transmit(TRANSMIT trans)
{
    LOG_DBG("HCI [%s]n",__FUNCTION__);
    MESSAGE_T * message = (MESSAGE_T *)trans;
    LOG_DBG("HCI %s a[%d] b[%d] c[%c]n",__FUNCTION__,message->a,message->b,message->c);
}
static setcallback(BteCallback *cb)
{
    callback = cb;
    callback->cB(123456);
}

static const bt_hc_interface_t bluetoothHCIInterace = {  
    sizeof(bt_hc_interface_t),
    init,
    setcallback,
    transmit,
};


const bt_hc_interface_t * getbluetoothHCIInterace()
{
    LOG_DBG("HCI [%s][%x]n",__FUNCTION__,&bluetoothHCIInterace);
    return &bluetoothHCIInterace;
}

---------------------------------
mainc.c

#include"bte.h"
#include"hci.h"
int main()
{
    BTE * bte = getBTE();
    bte->Init_T();
    bte->Option_T(0);
    bte->Option_T(1);
    bte->Option_T(12);
}
7.3.10 举例
char * const *(*next)();
char *(* c[10])(int **p);
void (*signal(int sig,void(*func)(int)))(int)
7.4 字符串
字符串练习
#include
#include
//字符串练习

void mystrcat(char *dest,char *str) {
    char *tmp = dest;
    while (*tmp!='')
    {
        tmp++;
    }
    while (*str!='')
    {
        *tmp = *str;
        tmp++;
        str++;
    }
    *tmp = '';
}
int main() {
    printf("---------------1.在栈中定义--------------n");
    char str[] ="abcdjkajdfkajk";//str 在栈中 在运行时期确定其值,可以改变
    char str2[] ={"agdagagagf"};
    char str3[4]={'c','b','c'};//默认后面有''
    char str4[10]={'a','b','c','d','','5','r'};
    printf(" - str  [%s] [%x] [%d] [%d] - n",str,str,sizeof(str),strlen(str));//打印:[abcdjkajdfkajk] [15] [14]
    printf(" - str2 [%s] [%x] [%d] [%d] - n",str2,str2,sizeof(str2),strlen(str2));
    printf(" - str3 [%s] [%x] [%d] [%d] - n",str3,str3,sizeof(str3),strlen(str3));
    printf(" - str4 [%s] [%x] [%d] [%d] - n",str4,str4,sizeof(str4),strlen(str4));
  
    printf("---------------2.在字符常量中定义--------------n");
    char  *str5 = "zxcvbnm"; //str5 在栈中,指向字符常量 在编译时就确定了,不可改变其值
    char  *str6 = "uioxcv";
    printf(" - str5 [%s] [%x] [%d] [%d] - n",str5,str5,sizeof(str5),strlen(str5));// 8 7
    printf(" - str6 [%s] [%x] [%d] [%d] - n",str6,str6,sizeof(str6),strlen(str6));//8 6

    char *str7 = "UI";
    char *str8 = "UI"; 
    if(str7 == str8) {
         printf(" - str7 [%x] == [%x] str8 - n",str7,str8);
    }
    if(strcmp(str7,str8) == 0) {
        printf("n--相等--n");
    }else
    {
        printf("n--不相等--n");
    }
    

    printf("---------------3.在字符串输入  fgets--------------n");
    char str9[10];
    fgets(str9,10,stdin);//fgets 自动回加上n   实际的长度n 也占1个strlen
    printf("str9:[%s] [%d] [%d]n",str9,sizeof(str9),strlen(str9));
    if(str9[strlen(str9)-1] == 'n') {
        str9[strlen(str9)-1] = '';
    }
    printf("str9:[%s] [%d] [%d]",str9,sizeof(str9),strlen(str9));
    printf("n---------------4.在字符串输入  fputs--------------n");
    char str10[10]="123456";
    
    printf("n---------------5.在字符数组赋值 --------------n");
    char str11[10]="abcd";
    //赋值方式1
    for(int i=0;ik",10);
    printf("str12[%s]  [%d]n",str12,sizeof(str12));
    
    printf("n---------------6.在字符串的拼接 --------------n");
    char dest[100] = "123456789";
    char src[20] = "agnaigfnafgmaofmga";
    mystrcat(dest,src); //strcat 函数
    printf("[%s] [%d]n",dest,strlen(dest));
    //错误方式  dest src在运行期已经确定了,其值不可改变,只能改变它的指向
    //char *dest = "123456789";
    //char *src = "agnaigfnafgmaofmga";  
    //mystrcat(dest,src);

    printf("n---------------7.在字符串输入 --------------n");
    char str13[10];
    char *str14 ;
    scanf("%s",str13); 
    //scanf("%s",str14);  //野指针没有指向任何一个地址,程序会挂
    printf("%s",str13);

    printf("n---------------8.在字符串和指针数组 --------------n");
    char str15[4][10] = {"abcd","1234","a1b2c3","uincvb"};
    char *str16[10];//指针数组 数组里的元素都是指针,存放的是地址
    for (size_t i = 0; i < 4; i++)
    {
        str16[i] = str15[i];
        printf("[%d] [%s]n",i,str16[i]);
    }

    return 0;
}
7.5 结构体
#include
#include
struct Books
{
   char title[128];
   char author[128];
   int BookID;
};
//结构体练习
//共用体
union UU
{
    char c;
    int i;
    double d;
};

int main() 
{
    printf("---------------结构体--------------n");
    //结构体字节长度 计算用字节对齐方式
    struct Books books;
    strncpy(books.title,"chinese",8);
    strncpy(books.author,"ss",3);
    books.BookID = 200;
    printf("title:[%s] author:[%s]  [%d]n",books.title,books.author,books.BookID);
    struct Books *p;
    p = &books;
    printf("title:[%s] author:[%s]  [%d]n",p->title,p->author,p->BookID);
    printf("title:[%s] author:[%s]  [%d]n",(*p).title,(*p).author,(*p).BookID);

    printf("size:[%d] [%d]  [%d]n",sizeof(books),sizeof(*p),sizeof(p));//260 260 8

    printf("n---------------共用体--------------n");
    union UU u;
    printf("size:[%d]n",sizeof(u));//8
    //共用体字节长度 是占最大字节的
    return 0;
}
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/776269.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号