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 once7.1.3 SDK 和NDK 区别:
sdk (java)
NDK(c/c++) 提供.so
预处理
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 文件翻译成二进制指令文件
链接
链接函数库,生成可执行文件
静态库
这类库的名字一般是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 650017.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 return7.2.3 数据类型
| 说明 | 字符型 | 短整型 | 整形 | 长整型 | 单精度浮点型 | 双精度浮点型 |
|---|---|---|---|---|---|---|
| 数据类型 | char | short | int | long | float | double |
| 长度 | 1 | 2 | 4 | 4 | 4 | 8 |
构造类型 数组 结构体 共用体 枚举 指针 void
7.2.4 字节大小
注意:
32 指针占4 字节 64 位指针占8字节
32为long4字节 64我long8字节
字符类型由单引号’ '包围,字符串由双引号" "包围。
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.在形式参数的函数参数定义中
当局部变量被定义时,系统不会对其初始化,必须要程序员对其初始化,定义全局变量时,系统自动会为其初始化。
extern.c #include7.2.9 宏定义的使用 7.2.9.1 C预处 理器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; }
C 预处理器不是编译器的组成部分,但是它是编译过程中一个单独的步骤。简言之,C 预处理器只不过是一个文本替换工具而已,它们会指示编译器在实际编译之前完成所需的预处理。我们将把 C 预处理器(C Preprocessor)简写为 CPP。所有的预处理器命令都是以井号(#)开头。它必须是第一个非空字符,为了增强可读性,预处理器指令应从第一列开始。下面列出了所有重要的预处理器指令:
| 指令 | 描述 |
|---|---|
| #define | 定义宏 |
| #include | 包含一个源代码文件 |
| #undef | 取消已定义的宏 |
| #ifdef | 如果已经宏已经定义了,则返回真 |
| #ifndef | 如果宏没有定义,则返回真 |
| #if | 如果给定条件为真,则编译下面带 |
| #else | #if替代方案 |
| #elif | 如果前面的#if给定条件不为真,当前条件为真,则编译下面代码 |
| #endif | 结束一个#if…#else条件编译块 |
| #error | 当遇到标准错误时,输出错误消息 |
| #pragma | 使用标准化方法,向编译器发布特殊的命令到编译器中 |
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个字节
&号 用来取一个对象的地址
*号 一个间接寻址符,用于访问指针所指向的地址的值。
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]的值
数组指针练习
数组指针本身就是二维指针,表示的是行指针, 指向一维二维数组的行指针。
数组指针指向一维时:
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_ #include7.3.10 举例#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); }
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;i


