本人最近学习ndk,涉及到C语言,所以写下这篇略显肤浅的笔记…大神请忽略此文…
一、HelloWorld
#include <stdio.h> // 包含标准的输入输出的头文件, 类似java的导包
#include <stdlib.h> // 包含标准的库函数
main() { // 入口函数
printf("hello World\n");
system("pause"); // 调用系统命令,暂停程序,以便查看运行结果
}
二、数据类型
C语言的数据类型包括基本数据类型、构造类型、指针类型、空类型。其中基本数据类型包括数值类型和字符类型。数值类型又包括整型和浮点类型。基本数据类型如下:
- char 1字节
- short 2字节
- int 4字节
- float 4字节
- long 4字节
- double 8字节
有几个注意事项:
-
C语言中没有byte, 可以用char表示
-
C语言中没有String类型,可以用字符数组或char类型的指针
-
C语言中没有boolean, 可以用整数0表示false, 1表示true
-
signed和unsigned: 数据类型修饰符,用来修饰char, short, int, long 如signed int
-
signed int简称int,signed short简称short,signed long简称long。注意signed char不等于char,这是两种类型了。
-
int32_t 其实就是int , int64_t其实就是long long
-
long long就等于java中的long ,8个字节
main() { long long ago; // sizeof获取某个数据类型的长度 printf("char的长度为:%d\n", sizeof(char)); printf("short的长度为:%d\n", sizeof(short)); printf("int的长度为:%d\n", sizeof(int)); printf("float的长度为:%d\n", sizeof(float)); printf("long的长度为:%d\n", sizeof(long)); // long的长度不会比int短 printf("double的长度为:%d\n", sizeof(double)); printf("long long的长度为:%d\n", sizeof(long long)); printf("signed int的长度为:%d\n", sizeof(signed int)); printf("unsigned int的长度为:%d\n", sizeof(unsigned int)); system("pause"); }
三、输入输出
%hd - short
%d - int
%ld – long
%c - char
%f - float
%lf – double
%u – 无符号数
%x – 十六进制输出
%#x- 十六进制输出,并输出0x表示十六进制
%o - 八进制输出
%#o - 八进制输出,并输出0表示八进制
%s – 字符串
输入:
int count;
//scanf会报一个不安全的错误,怎么办呢?
//方法一:在引入头文件之前 #define _CRT_SECURE_NO_WARNINGS //宏定义
//方法二:使用新函数scanf_f
scanf_s("%d",&count);//&表示取变量内存地址,把接收的值赋给count
printf("count=%d\n",count);
输出:
char c = 'a';
short s = 1;
int i = -1;
long l = 255;
float f = 3.14;
double d = 3.1415926;
printf("c = %c\n", c);
printf("s = %hd\n", s);
printf("i = %d\n", i);
printf("l = %ld\n", l);
printf("f = %f\n", f);//默认输出6位有效数字
printf("f = %.2f\n", f);// .2 输出两位有效数字
printf("d = %lf\n", d); // 默认输出6位有效数字,会四舍五入
printf("i = %u\n", i); // 无符号的数
printf("l = %#X\n\n", l); // 十六进制输出 0x
四、指针
指针就是地址,地址就是内存单元的编号;指针变量是存放地址的变量;指针与指针变量是两个概念,但现实中经常把指针变量说成指针。
指针使用中常见错误:
(1)野指针:没有赋值的指针,不能通过*修改对应内存的数据;
(2)数据类型不匹配:比如int类型的指针只能保存int变量的内存地址;
main() {
int i = 100;
printf("i的内存地址为:%#x\n", &i);
// 定义一个指针变量p,用来保存int变量i的内存地址
int* p; // int *p int * p;
// 把i的内存地址赋值给指针变量p
p = &i;
printf("i的内存地址为:%#x\n", p);
printf("i= %d\n", i);
printf("i= %d\n", *p); // *p表示取p保存的内存地址所存放的值,其实就是i的值
//区分: p 和 &p 和*p
printf("%#x\n", p); // i的内存地址
printf("%#x\n", &p); // 指针变量p的内存地址
printf("%d\n", *p); // i的值
system("pause");
}
#include <stdio.h>
#include <stdlib.h>
main() {
int i = 100;
int* p = &i; // 一级指针,保存变量的内存地址
int** p2 = &p; // 二级指针,保存一级指针的内存地址
int*** p3 = &p2; // 三级指针,保存二级指针的内存地址
printf("i = %d\n", i);
printf("i = %d\n", *p);
printf("i = %d\n", **p2);
printf("i = %d\n", ***p3);
system("pause");
}
指针的应用:
- 交换变量值
#include <stdio.h>
#include <stdlib.h>
/** 交换两个数的值 */
swap1(int i , int j){ // 形参
int temp = i;
i = j;
j = temp;
}
swap2(int* p, int* q){
int temp = *p;
*p = *q;
*q = temp;
}
main() {
int i = 100;
int j = 200; // 实数
printf("i = %d\n", i);
printf("j = %d\n", j);
// swap1(i, j); // 不会交换值
swap2(&i, &j); // 会交换两个数的值
printf("i = %d\n", i);
printf("j = %d\n", j);
system("pause");
}
- 返回多个值(更改变量值)
#include <stdio.h>
#include <stdlib.h>
fun(int* p, int* q) {
*p =7;
*q = 8;
}
main() {
int i =100;
int j =200;
printf("i = %d\n", i);
printf("j = %d\n", j);
fun(&i, &j);
printf("i = %d\n", i);
printf("j = %d\n", j);
system("pause");
}
- 主函数中获取子函数变量的内存地址
#include <stdio.h>
#include <stdlib.h>
fun2(int** q) {
int i = 100;
printf("i的内存地址为:%#x\n", &i);
*q = &i;
}
main() {
int i = 0;
int* p = &i;
fun2(&p);
// 变量还没来得及被回收,但马上被回收了
// printf("i的值为:%d\n", *p);
printf("i的内存地址为:%#x\n", p);
// 子函数执行完后,局部变量已经被回收
printf("i的值为:%d\n", *p);
system("pause");
}
指针与数组:
main() {
// char arr[] = {'a', 'b', 'c'};
char arr[] = "Hello";
printf("第1个元素的内存地址: %#x\n", &arr[0]);
printf("第2个元素的内存地址: %#x\n", &arr[1]);
printf("第3个元素的内存地址: %#x\n", &arr[2]);
int arr2[] = {1, 2, 3};
printf("第1个元素的内存地址: %#x\n", &arr2[0]);
printf("第2个元素的内存地址: %#x\n", &arr2[1]);
printf("第3个元素的内存地址: %#x\n", &arr2[2]);
// C语言中的数组越界了,不会报错
printf("第10个元素的值: %d\n\n\n", arr2[9]);
// 数组的首地址: 下面三种方式都是一样的
printf("数组的首地址: %#x\n", &arr[0]);
printf("数组的首地址: %#x\n", &arr);
printf("数组的首地址: %#x\n\n", arr);
// 通过指针变量操作数组
char* p = arr;
printf("第1个元素的值: %c\n", *p);
printf("第2个元素的值: %c\n", *(p+1)); // 往右偏移一个单位, 对于char就是1个字节
printf("第3个元素的值: %c\n\n", *(p+2));
printf("第1个元素的值: %c\n", p[0]);
printf("第2个元素的值: %c\n", p[1]);
printf("第3个元素的值: %c\n", p[2]);
int* q = arr2;
printf("第1个元素的值: %d\n", *q);
printf("第2个元素的值: %d\n", *(q+1)); // 往右偏移一个单位, 对于int就是4个字节
printf("第3个元素的值: %d\n\n", *(q+2));
// p - q; // 没有意义
system("pause");
}
五、动态分配内存
栈内存:在栈中分配也叫静态分配;栈内存由系统自动那个分配自动回收;空间大小固定,超出会报栈溢出;速度快。
堆内存:在堆中分配也叫动态分配;堆内存需要调用malloc分配,调用free回收;内存有多大,堆就可以有多大;速度相对慢一些。
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
main() {
int count = 5;
// 在堆中创建连续的内存空间
int* p = malloc(sizeof(int) * count);
// 对分配出来的内存空间进行赋值
int i = 0;
for(; i < count; i++) {
// 对每个元素进行赋值
printf("第输入第%d个元素:", (i+1));
scanf_s("%d", &p[i]); // (p+i)
}
i = 0; // 输出每一个元素
for(; i< count; i++) {
printf("第%d个元素为:%d\n", (i+1), p[i]); // *(p+i)
}
// 释放内存
free(p);
printf("第1个元素为:%d\n", p[0]); // 打印第一个元素
system("pause");
}
多次调用malloc这个函数分配的空间是不连续的,若要连续,需要用realloc,用法如下:
.......//省略
int* p = malloc(sizeof(int) * count);
// 再次分配内存,与上一次分配出来的内存连续
// 参1: 上一次分配出来的内存空间的首地址
// 参2: 总共要分配多少内存
p = realloc(p, sizeof(int) * (count + newCount));
printf("p = %#x", p);
.......//省略
六、函数指针
函数指针是指向函数的指针变量,本质是一个指针变量,保存函数的内存地址。
#include <stdio.h>
#include <stdlib.h>
int add(int i , int j) {
return i+j;
}
main() {
// 定义一个函数指针,用来保存函数的首地址
int (*pf)(int, int);
// 把函数指针进行赋值, 把add函数的首地址赋值给函数指针pf
pf = add;
// 通过函数指针调用对应的函数
int result = pf(3, 5);
printf("result = %d\n", result);
system("pause");
}
七、结构体
结构体的长度不是简单地相加,有一个补齐规则:
(1)结构体的长度并不是所有成员长度相加,在编译时长度会进行补齐以方便位移。
(2)结构体长度补齐规则:
结构体成员的偏移量是成员长度的整数倍, 如果不是则补齐
结构体的总长度是所有成员长度的整数倍,如果不是则补齐;
(3)成员的偏移量即:成员的地址与结构体首地址的差值。
定义方式有三种:
- 先定义结构体类型,再定义结构体类型变量
struct Student
{
int age;
float score;
char sex;
}
main() {
struct Student s1;
}
- 定义结构体的同时定义结构体类型变量
struct Student2
{
int age;
float score;
char sex;
} s1;
//也可以再定义如下变量:
main() {
struct Student s2;
}
- 直接定义结构体类型变量
struct
{
int age;
float score;
char sex;
} s1;
例子:
#include <stdio.h>
#include <stdlib.h>
void study() {
printf("good good study...\n");
}
struct Student
{
int age; // 4
float score; // 4
char sex; // 1
double d; // 8
// 定义函数指针
void (*studyP)();
}
main() {
// 结构体初始化
// 方式1:
struct Student s1 = {20, 100, 'f', 0, study};
// 方式2:
struct Student s2;
s2.age = 25;
s2.score = 90;
s2.sex = 'm';
s2.studyP = study; // 函数指针赋值
// 访问结构体的成员
printf("s1.age = %d\n", s1.age);
printf("s1.score = %f\n", s1.score);
printf("s1.sex = %c\n\n", s1.sex);
printf("s2.age = %d\n", s2.age);
printf("s2.score = %f\n", s2.score);
printf("s2.sex = %c\n\n", s2.sex);
// 结构体的长度:
printf("结构体的长度: s1 = %d\n", sizeof(s1));//24
// 调用函数指针(三种方式)
// 方式1:
s1.studyP();
// 定义结构体指针变量
struct Student* p;
p = &s1;
// 方式2:
(*p).studyP();
// 方式3
p->studyP(); // 转转换成(*p).studyP()
system("pause");
}
八、联合体
当多个数据需要共享内存时可以使用联合体,联合体的成员在赋值时会相互覆盖,因为它们首地址一样。
#include <stdio.h>
#include <stdlib.h>
union U
{
float f; // 4
int i; // 4
char c; // 1
}
main() {
union U u;
u.i = 100;
u.c = 'a';
printf("u.i = %d\n", u.i); //97
printf("联合体的长度: %d\n", sizeof(u));//4
system("pause");
}
九、关键字typedef
#include <stdio.h>
#include <stdlib.h>
// 给int起一个别名,叫做jint
typedef int jint;
main() {
jint aa = 1;
printf("aa = %d\n", aa);
system("pause");
}