学习C和C++对于Android程序员学习NDK来说,非常重要,今天就从手撕HelloWord!开始C和C++的学习!希望对兄弟们有点用处!
1、main函数的结构体
#include <stdio.h>
//<>寻找系统的资源
// "" 寻找我们自己写的资源
//.h .hpp (声明文件 头文件)
// .c .cpp(实现文件)
//代码结构和 基本数据类型
int main(void) { //主函数入口
printf("Hello, World!\n");
return 0;
}
2、常用基本数据类型和printf打印
在C语言中,使用控制台打印和Java的有所不同,打印变量,需要使用占位符进行占位,有点类似于,在写SQL的时候,? 进行占位
int mainT1(void) { //主函数入口
int numberInt =100;
double nuberDouble =200;
float numberFloat =100;
long numberLong = 300;
short numberShort=100;
char c = 'a';
char * str= "一杯凉白开";
//printf(numberInt);//这样是打印不出来的,c打印需要占位符
printf("numberInt的值:%d\n",numberInt);
printf("nuberDouble的值:%lf\n",nuberDouble);
printf("numberFloat的值:%f\n",numberFloat);
printf("numberLong的值是%ld\n",numberLong);
printf("numberShort的值:%d\n",numberShort);
printf("c的值%c\n",c);
printf("str的值%s\n",str);
//基本数据类型所占字节数
printf("int 数据类型所占的字节数:%d\n",sizeof(int));
printf("double 数据类型所占字节数:%d\n",sizeof(double));
printf("float 数据类型所占字节数:%d\n",sizeof(float));
printf("long 数据类型所字节数:%d\n",sizeof(long));
printf("short 数据类型所占字节数:%d\n",sizeof(short));
printf("char 数据类型所占字节数:%d\n",sizeof(char));
return 0;
}
运行结果
3、C和C++ 万物皆指针 辨析 指针,指针变量 和内存地址
#include <stdio.h> //标准库
// C C++(对象) 万物皆指针
//Java 万物皆对象
//Linux 万物皆文件
int main(void) {
//指针 == 地址
//%p 地址输出的占位
//&number 取出number 的地址
int number = 1000; // 类型为int 别名为number 的变量 值 为100
printf("number变量的地址:%p\n",&number);//输出的地址是 74565ffc1c
//方法二 通过地址去取值
//*地址,取出该地址的里的内容
printf("number的值:%d\n",*(&number));
int * intp = &number;//类型为 int * (int类型的指针) 别名为 intp 的值是 number 的内存地址74565ffc1c
printf("number的值为:%d\n",*intp);
return NULL;
}
为了方便大家理解,画上一个图
int number =1000 在内存区域,开辟一块空间, 存放一个1000,这一块的地址是74565ffc1c
&number 取出这块空间的地址 74565ffc1c
*(&number)先执行括号里边的,拿出这块空间的地址,在执行外边的 拿出这个地址所在空间的值 1000
int * intp = &number; 在内存区域开辟另一块空间,内存地址为0x356 这个空间只能存放 存放了int值的内存地址
4、交换两个变量的值
void change(int i);
int mainT5() {
int number= 100;
int * p =&number;
*p =200;
change(number);
printf("%d",number);
return 0;
}
//函数不能写在main的下边,面向过程的,在main下边,main中调用的时会找不到,如果非要写在下边,需要先声明,
void change(int i) {
i =200;
}
这样写是改变不了number的值的,在change函数进栈时,int i 新的内存地址,值是number 的值,相当于number 的副本,你在改变change里边的i的时候,只是改变新内存地址的值,和number并没有半毛钱关系
# include <stdio.h>
void change();//C不支持函数重载,所以在声明函数时,可以不写形参
int main() {
int a = 100;
int b =200;
change(&a,&b);//传入 a的内存地址,和b的内存地址
printf("a的值:%d,b的值:%d",a,b);
return 0;
}
void change(int* a , int* b) {
int temp =* a;//拿到*a的值赋值给temp
*a = *b; //拿到b的值赋给 a
*b = temp;//将temp 赋值给b
}
所以想要通过方法改变,变量的值,我们需要传入指针,也就是内存地址,然后在方法内通过内存地址改变里边的内容,上面的代码不好理解的话,就看图
5、多级指针
指针存放的是内存地址,但是自己也有内存地址
#include <stdio.h>
int main(void) {
int number = 100;
//一级指针
int * number_p = &number;
//二级指针 在平时开发中,我们最多用到三级指针,如果说他开发了一东西,用到了八级指针,那就是在装逼哈哈
int * * number_p_p =&number_p;
int * * * number_p_p_p =&number_p_p;
//分别打印上面两个指针
printf("number_p:%p\n",number_p);
printf("number_p_p:%p\n",number_p_p);
printf("number_p_p_p:%p\n",number_p_p_p);
//如果我想打印 number的值,可以 这样打印
printf("一级指针获取number的值:%d\n",*number_p);
printf("二级指针获取number的值:%d\n",**number_p_p);
printf("三级级指针获取number的值:%d\n",***number_p_p_p);
return 0;
}
这一段代码,能帮助我们更好的理解多级指针
int number = 100
别名为number,类型为int ,值为100,内存地址是 78f2bffa14
int * number_p =&number
别名为number_p,类型为int型的一级指针,值为78f2bffa14 内存地址 是78f2bffa08
int ** number_p_p =&number_p
别名为number_p_p,类型为int型的二级指针,值为78f2bffa08 内存地址是78f2bffa00
**number_p_p
*number_p_p 拿出 内存地址为 78f2bffa08 的值 78f2bffa14
**number_p_p 拿出内存地址为 78f2bffa14 的值 100
6、数组与数组指针
#include <stdio.h>
//数组与数组指针
int main() {
//定义数组
//int [] arr ={1,2,3,4}; 这是java的写法,和C并不一样
int arr [] ={1,2,3,4};
//遍历数组 这种写法,在Clion上不报错,在Linux上会报错
// for (int i = 0; i < 4; ++i) {
//
// }
//比较规范的写法
int i = 0;
for ( i = 0; i < 4; ++i) {
printf("%d\n",arr[i]);
}
//数组指针
//数组的内存地址 == 数组里第一个元素的内存地址 打印arr 输出的也是 arr的内存地址
printf("数组%p\n",arr);
printf("数组的内存地址%p\n",&arr);
printf("数组第一个元素的内存地址%p\n",&arr[0]);
//挪动指针
int * arrp = &arr;
//数组第一个值
printf("数组第一个元素的值:%d 内存地址:%p\n",*arrp ,arrp);
//输出数组第二个值,挪动指针
arrp ++;
printf("数组第二个元素的值:%d 内存地址:%p\n",*arrp,arrp);
arrp ++;
printf("数组第三个元素的值:%d 内存地址:%p\n",*arrp,arrp);
arrp ++;
printf("数组第四个元素的值:%d 内存地址:%p\n",*arrp,arrp);
arrp -= 3;
printf("数组第一个元素的值:%d 内存地址:%p\n",*arrp,arrp);
arrp +=1000;//超出数组长度并不会报错,这是一个野指针,他的值是个系统值
printf("超出数组长度:%d\n",*arrp)
}
在C语言里,数组的内存地址是数组第一个元素的内存地址,打印arr 也是数组的内存地址,然而数组的内存地址是数组第一个元素的内存地址,所以他们三个打印出来时相同的。
打印arr 也是数组的内存地址 这句话和java里有点像,java打印数组或者对象的时候,也是打印的一个地址,如果想打印具体的值,还得重写对象的toString方法。
在我们写代码的过程中,发现指针是有类型的,int * ,double *,他们的大小都是一样的,指针占用内存大小 32位4字节,64位是8字节,既然大小都一样,为什么要区分类型呢?
移动指针的时候,指针+1,是移动到下个元素的地址,四个元素的地址打印出来了,大家是否发现了他们的规律,后一个都比前一个大4,然而int的大小就是4个四节,如果把数组的类型换成double,打印出来的地址也是这样的规律,后一个元素的内存地址,比前一个大8,double的大小也是8个字节。所以指针挪动的是根据指针类型进行挪动的,所以我们在循环的时候可以这样写
int main() {
int arr [4];
int * arrp =&arr;
int arrLength = sizeof(arr)/sizeof(int);
int i =0;
for (i = 0; i < arrLength; ++i) {
*(arrp+i) = 10+i;
printf("arr[%d]:%d\n",i,arr[i]);
}
}
7、函数指针
函数指针,函数也是有指针的,其用法类似Java里的接口回调,老规矩,先上例子,
# include <stdio.h>
void add(int a,int b) {
printf("a+b的值为:%d",a+b);
}
void mins(int a ,int b) {
printf("a-b的值为:%d",a-b);
}
// 操作 回调到 add,mins
//void(*method)(int,int) void 返回值 (*method) 函数名字, (int,int)两个形参
caluteNumber(void(*method)(int,int), int number1,int number2) {
method(number1,number2);//回调传进来的方法
}
int main () {
caluteNumber(add,10,20);
caluteNumber(mins,20,10);
return 0;
}
通过例子,我们通过函数指针的方式,去回调了两个不同的方法!下边我们分别打印方法,和方法的地址
# include <stdio.h>
void add(int a,int b) {
printf("a+b的值为:%d\n",a+b);
}
void mins(int a ,int b) {
printf("a-b的值为:%d\n",a-b);
}
// 操作 回调到 add,mins
//void(*method)(int,int) void 返回值 (*method) 函数名字, (int,int)两个形参
caluteNumber(void(*method)(int,int), int number1,int number2) {
method(number1,number2);//回调传进来的方法
printf("method :%p\n",method);
printf("&method:%p\n",&method);
printf("add:%p\n",add);
printf("&add:%p\n",&add);
printf("mins:%p\n",mins);
printf("&mins:%p\n",&mins);
}
int main () {
caluteNumber(add,10,20);
caluteNumber(mins,20,10);
return 0;
}
首先, add 和 &add的值是一样的,mins 和&mins的值是一样的,和数组有些类似,数组 是 arr 和 &arr 是一样的事数组首元素的地址,所以方法就是个指针,(先这样理解)
void(*method)(int,int) 他是个指针 存放的是方法的地址,所以打印出来methd的值是add的地址,然后这个指针,也有它自己的地址 00000075075ffd40
8、总结
从刚刚的体验来说,C(万物皆指针)和Java(万物皆对象)还是有很大的区别的,在以后得工作中,我们要经常和指针打交道,所以指针还是挺重要的,我们辨析了什么是指针,指针变量,多级指针,数组指针,和函数指针!