数据类型
基本数据类型
- java基本数据类型
* byte 1个字节
* short 2个字节
* int 4个字节
* long 8字节
- 浮点型
* float 4个字节
* double 8字节
- 字符型
* char 2字节
- boolean true/false
C的基本数据类型
- 整型
* short int
* int
* long int
* long long int
- 浮点型
* float
* double
- 字符型
* char
格式化输出
- short类型:
short a = 123;
printf("%hd\n", a);
- int类型
int b = 123;
printf("b = %d\n", b);
- long 类型
long int c = 345;
printf("c = %ld", c);
- long long 类型
long long d = 123456;
printf("d=%lld\n", d);
- float 类型
float e = 1.234;
printf("e=%f\n", e);//默认6位有效数字
printf("e = %.2f\n", e);//保留两位有效数字
- double 类型
double f = 2.34567;
printf("f = %lf\n", f);//默认6位有效数字
printf("f = %.4lf\n", f);//保留四位有效数字
- 字符类型
char g = 'a';
printf("g = %c\n", g);
各种数据类型在内存中的大小
- 判断变量在内存中的大小
int a = 12;
int size = sizeof(a);
printf("sizeof(int) = %d\n", size);
- 基本数据类型在内存中的大小
sizeof(int) = 4;
sizeof(short) = 2;
sizeof(long) = 4;
sizeof(long long) = 8;
sizeof(float) = 4;
sizeof(double) = 8;
sizeof(char) = 1;
- 0为假, 非零为真。
if(0){//假
}else{
}
指针 直接操作内存区域
- 指针赋值和修改值
int a = 123;
int *p;
p = &a;//将a的地址取出来, 然后赋值给p
printf("a=%d\n",*p);
*p = 234;//将234赋值给指针p
printf("a=%d\n", a);
- 交换两个数的值
int a = 0;
int b = -1;
int *p1 = &a;
int *p2 = &b;
*p1 = *p1^*p2;
*p2 = *p1^*p2;
*p1 = *p1^*p2;
printf("a =%d, b = %d\n", a, b);
- 多级指针
void test(){
int a = 1;
int *p1 = &a;//一级指针
int **p2 = &p1;//二级指针
int ***p3 = &p2;//三级指针
int ****p4 = &p3;//四级指针
printf("*p1 = %d\n", *p1);
printf("**p2 = %d\n", **p2);
printf("***p3 = %d\n", ***p3);
printf("****p4 = %d\n", ****p4);
}
- 函数指针
int add(int a, int b){
return a+b;
}
int minus(int a, int b){
return a-b;
}
//指针当做函数参数
int compute(int a, int b, int(*pf)(int, int)){
return pf(a, b);
}
void test2(){
//定义函数指针变量p,p指向一个函数,
//函数的返回值必须是int, 函数的形参
int (*p) (int, int);
p = &add;
p = −
int result = p(2, 3);
//使用方式一
int result = compute(2, 3, p);
//使用方式二
result = compute(2, 3, &add);
printf("result = %d\n", result);
}
- 野指针 没有任何指向的指针
int *p;
*p = 123;
printf("*p = %d\n", *p);
数组
- 数组定义
void test3(){
//数组定义
int arr1[10];
int arr2[] = {1, 2, 3, 4, 5};
int arr3[10] = {1, 2, 3};
int len1 = sizeof(arr1)/sizeof(int);
int len2 = sizeof(arr2)/sizeof(int);
int len3 = sizeof(arr3)/sizeof(int);
printf("%d\n", len1);
printf("%d\n", len2);
printf("%d\n", len3);
for(int i=0; i<len2; i++){
printf("arr2[i] = %d\n", arr2[i]);
}
}
- 数组的遍历
void print_arr(int arr[], int len){
for(int i=0; i<len; i++){
printf("arr[%d] = %d\n", i, arr[i]);
}
}
void print_arr2(int *p, int len){
for(int i=0; i<len; i++){
//通过索引取值
//printf("arr[%d] = %d\n", i, p[i]);
//通过地址值取值
printf("arr[%d] = %d\n", i, *(p+i));
}
}
void test4(){
int arr[] = {1, 6, 3, 4, 67};
int len = sizeof(arr) / sizeof(int);
print_arr(arr, len);
print_arr2(arr, len);
}
- 数组的排序
void test5(){
int arr[] = {1, 6, 3, 4, 67};
int len = sizeof(arr)/sizeof(int);
for(int i=0; i<len-1; i++){
for(int j=0; j<len-1-i; j++){
if(arr[j] > arr[j+1]){
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
for(int k=0; k<len; k++){
printf("arr[%d] = %d\n", k, arr[k]);
}
}
- 内存模型 linux环境下
- Text段
- data段
stack
heap
static data//已经初始化的常量或全局变量 - bss段 未初始化的常量和全局变量
- 内存申请和释放 动态数组
#include <stdio.h>
#include <stdlib.h>
void test6(){
int len = 3;
// void* p = malloc(len*sizeof(int));
//申请内存
int *p1 =(int*) malloc(len*sizeof(int));
p1[0] = 0;
p1[1] = 1;
p1[2] = 2;
int i = 0;
for(; i<len; i++){
printf("p1[%d] = %d\n", i, p1[i]);
}
//释放空间
free(p1);
}
int main(){
test6();
}
结构体
- 结构体定义方式
#include <stdio.h>
#include <stdlib.h>
//第一种定义方式 结构体
struct Student{
char *name;
int age;
double score;
};
//第二种定义方式
struct Person{
char *name;
int age;
} person;
//第三种定义方式 匿名结构体
struct {
char *name;
int age;
};
int main(){
//结构体赋值
struct Student stu = {"张三", 23, 99.5};
printf("stu.name = %s, stu.age=%d, stu.double=%.2lf\n", stu.name, stu.age, stu.score);
person.name = "wwf";
person.age = 12;
printf("name = %s, age = %d\n", person.name, person.age);
}
- 结构体指针
struct Student{
char *name;
int age;
double score;
};
int main(){
//结构体赋值
struct Student stu = {"张三", 23, 99.5};
struct Student *p = &stu;
(*p).name = "龙飞";
(*p).age = 99;
printf("stu.name = %s, stu.age=%d, stu.score=%.2lf\n", stu.name, stu.age, stu.score);
//间接引用运算符
printf("stu.name = %s, stu.age = %d, stu.score = %.2lf\n", p->name, p->age, p->score);
//二级指针
struct Student **p2;
p2 = &p;
printf("stu.name = %s, stu.age = %d, stu.score = %.2lf\n", (**p2).name, (**p2).age, (**p2).score);
printf("stu.name = %s, stu.age = %d, stu.score = %.2lf\n", (*p2)->name, (*p2)->age, (*p2)->score);
}
- 结构体大小
内存开辟为2的整数倍, 各个数据类型的大小之和
struct Student{
char *name;//4byte
int age;//4byte
double score;//8byte
};
大小为16byte
- 结构体中添加方法
int add(int a, int b){
return a+b;
}
// 结构体
struct Student{
char *name;//4byte
int age;//4byte
double score;//8byte
int (*Add)(int, int);
};
int main(){
//结构体赋值
struct Student stu = {"张三", 23, 99.5};
stu.Add = &add;
int result = stu.Add(1, 2);
printf("result = %d\n", result);
}
联合体
#include <stdio.h>
#include <stdlib.h>
union Type{
char *a;
int b;
double c;
};
int main(){
union Type t;
t.a = "abc";
t.c = 124.1234;
return 0;
}
- 联合体和结构体区别
结构体大小, 是所有数据大小之和,
联合体共享内存. 采用最大的那个数据类型大小, 联合体赋值一次, 赋值多次没有意
义, 因为后面的会覆盖前面的数据.
枚举
enum DAY{
Mon, Tues, Web, Thur, Fri, Satu, Sun
};
int main(){
enum DAY today = Sun;
switch(today){
case Mon:
break;
case Tues:
break;
case Web:
break;
}
return 0;
}
typedef(给类型起别名)
typedef int size;
//给别名起别名
typedef size length;
typedef length width;
typedef length height;
struct Student{
char *name;
int age;
};
typedef struct Student *Stu;
int main(){
int a = 123;
size b = 123;
width c = 1;
height d = 2;
Stu s = {"玲玲", 21};
return 0;
}
宏定义(#define)
#define PI 3.1415926;
//方法模板
define AREA(r) PI * r * r;
int main (){
double result = AREA(2);
printf("result = %lf\n", result);
}
c语言语法
一. 字符串格式化输出
- %d 整数 int类型
- %s 字符串类型
- %f float类型
- %.3lf double类型, 保留3位小数
- %p 指针类型
- %% 打印一个百分号
- %c 字符类型
- \0 不可见字符, 字符串结尾, 存储结束
- %hd short int 类型
- %u unsigned int
- %lu unsigned long
二. 随机数
- arc4random_uniform(n)产生一个0到n-1的随机数
- 10-20的随机数, 最大数-最小数+1 + 最小数 如下:
arc4random_uniform(20-10+1) + 10
三. goto语句
- 使用方法: 标签名: 语句 在需要跳转的地方, goto 标签名, 容易造成死循环, 如下
- 可以向前跳, 也可以向后跳.
- 只能在当前函数中使用
- 标签名下面的第一行代码, 不能定义变量
int main(int argc, const char * argv[])
{
loop:
// insert code here...
printf("Hello, World!33333\n");
float f = 1.234554654;
printf("f = %.2lf\n", f);
int r = random2(10);
if(r<6)
{
goto loop;
}
printf("r = %d\n", r);
return 0;
}
四. xcode调试
- 调试时, 高亮选中的代码是一个函数, 按下step over, 跳过函数, 执行下一步
- 如果, 想进入函数, 按下step in
五. 原, 反, 补码
- 正数, 原反补码, 一样
- 负数, 原反补码, 不一样
- short int num = 12; 在内存中两个字节
- int num = 1234; 四个字节
- long int num = 213435554; 8个字节 64位系统, 4个字节 32位系统
六. 垃圾值的由来, 我们声明一个局部变量, 在内存回收的时候, 只是标记不在使用,
而不会清理内存中的数据, 再次分配给其他变量时, 数据会分配给新的变量.
七. 数组初始化方式
-
先声明, 再赋值
int arr[3]; arr[0] = 1; arr[1] = 2; arr[2] = 3;
-
声明数组时, 初始化数组元素, 也可以省略数组长度
int arr[3] = {1, 2, 3}; int arr[] = {1,, 2, 3};
赋值时, 指定数组元素个数, 后面大括号中的元素可以不等于数组长度, 会自动截取, 或者补0
```
int arr[3] = {1, 2, 3, 4, 5};
int arr[3] = {1};
```
-
指定下标的初始化
int arr[3] = {[1]=2, [2]=3, [3]=4};
-
二维数组, 行数可以省略, 列数不能省略.
int arr[][4] = {1, 5, 3, 4, 6, 3, 8, 0, 8, 9, 12, 45, 23}; //行数 int rows = sizeof(arr) / sizeof(arr[0]); //列数 int cols = sizeof(arr[0]) / sizeof(arr[0][0]);
八. 字符串
字符串长度的计算 从左向右遍历, 遇到\0结束.
-
常用函数
// 1, puts()函数, 输出字符串, 自动换行, 只能输出字符串, 也不能使用占位符 char name[] = "jack"; puts(name); // 2, gets()函数 作用: 从控制台接收一个字符串数据, 与scanf的区别, // 可以接收空格, 和scanf()函数一样, 不安全 char name[10]; printf("请输入你的姓名: "); gets(name); puts(name); // 3, #include <string.h> // strlen(str) 字符串长度 char name[20] = "237748895985"; long len = strlen(name); printf("len = %lu\n", len); // 4, int strcmp(str1, str2) compare 比较字符串 // 等于0 相等 // 大于0 str1大 // 小于0 str2大 char name1[12] = "345454"; char name2[12] = "2345465768"; int num = strcmp(name1, name2); if(num==0){ } // 5, strcpy(str1, str2)函数 copy, 将str1 拷贝到str2中\0也拷贝 // 6, strcat(str1, str2)函数. concat: 链接 将str2连接到str1上 // 7, fputs(str, stdout); file 将字符数据输出到指定的流中 标准输入流和标准输出流 stdin, stdout // 8, fopen(path, mode)函数, 可以创建一个指向文件的指针. FILE * pFile = fopen("/Users/wwf/a.txt", "a"); char *name = "黑马程序员"; fputs(name, pFile); fclose(pFile); // 9, fclose(file pointer)关流 // 10, fgets()函数, 从控制台接收用户输入字符串 scanf()缺点, 不安全, 输入的空格会被认为结束 gets()缺点, 不安全 fgets(<#char *restrict#>, <#int#>, <#FILE *#>) 参数一: 将字符串存到那个数组中 参数二: 接收字符串的长度 参数三: 指定流 // 11, 从文件流中读取内容 FILE * pFile = fopen("/Users/wwf/a.txt", "r"); char name[50]; fgets(name, 50, pFile); fclose(pFile); printf("name = %s\n", name);
九. 指针: 变量的地址 指针在内存中占据8个字节
-
指针变量 存储指针的变量, 只能用来存储地址
int *p1; int num = 10; p1 = # // p1的值 printf("p1 = %p\n", p1); // p1的地址 printf("p1 = %p\n", &p1);
*p1 代表指针指向的变量
-
批量声明指针
int *p1, *p2, *p3; // NULL 不指向任何地址, 完全等价于0 int *p1 = NULL;
野指针 声明一个指针变量, 未初始化, 是一个垃圾值, 随机的.
未初始化, 直接使用.-
多级指针
- 二级指针: 指针变量存储的是, 一级指针的地址,
- 三级指针: 指针变量存储的是, 二级指针的地址.
- 一级指针, 只能存储普通变量的地址
- 二级指针, 只能存储一级指针变量的地址
- 三级指针, 只能存储二级指针变量的地址
- n级指针, 只能存储n-1级指针变量的地址
int num = 12; int *p1 = # int **p2 = &p1; int ***p3 = &p2;
-
指针的运算
- 指针可以和整数进行加减运算
- 指针+1 并不是+1, 而是增加一个单位变量所占用的字节数
- 指针之间只能做减法运算, 其他运算没有意义
-
指针遍历数组的元素
int arr[] = {3, 6, 3, 9, 23}; int *p = arr; int len = sizeof(arr)/sizeof(arr[0]); for(int i = 0; i < len; i++) { printf("arr[%d] = %d\n", i, *(p++)); }
-
指针与指针之间的减法运算, 结果是一个long类型的数据
结果的意义代表两个指针变量之间相差多少个单位变量
-
指针与指针之间,比较运算
内存分配时, 先分配高字节, 再分配低字节.
十. 内存中的五大区域
堆
栈
BSS段
未初始化的全局变量和静态变量, 程序在刚运行的时候,未初始化.数据段/常量区
全局变量初始化为0后, 存到常量区.
已经初始化的全局变量, 静态变量和常量等.代码段
用来存储程序的代码/指令-
字符数组和字符指针的区别
// 都是以字符串数组存储 //可以修改值 char arr[] = "jkklg"; //不可以修改值 char *arr1 = "dff"; // 字符串恒定性, 以字符指针形式存储的字符串.
-
从文件流中读取字符串
FILE * pFile = fopen("/Users/wwf/a.txt", "r"); char name[50]; fgets(name, 50, pFile); fclose(pFile); printf("name = %s\n", name);
十一. const关键字 常量
-
const 修饰数组
int arr[4] = {1, 4, 6, 8}; //数组元素的值不能修改
-
const 修饰指针
// 无法通过p指针去修改指针指向的变量的值. 直接操作变量可以 int num = 14; const int *p = # // 可以把另外一个的变量的地址, 赋值给p int num2 = 15; p = &num2; // p的值不能改, 但是可以通过p修改指向的变量的值. int * const p = # *p = 16; // 都不能该 const int * const p = #
十二. 如何申请堆空间内存
三个函数: malloc(size_t) unsigned long类型, 申请, calloc(size, sizeof(int))申请, realloc()释放; 申请多少个字节空间, 在stdlib.h头文件中
堆中的空间是, 从低字节开始分配的,不一定连续, 指的是多次molloc()分配内存, 每一次分配的字节数是连续的; 栈中的空间, 从高字节开始分配, 连续的.
-
判断是否申请成功,
int *p = malloc(4); if(p)//申请成功 等价于p!=NULL { }
-
calloc函数:
- 作用: 向堆空间申请指定字节数的空间
- 格式: 参数1: 多少个单位; 参数2: 每一个单位的字节数
- calloc(3, sizeof(int))
- 与malloc相比的优势, 申请内存后, 系统会将字节中的数据清零.
-
realloc函数
扩容
注意: 指针 几乎可以操作内存上的每一个字节. 但是我们还是建议, 不要乱来,
只操作我们申请的字节空间, 因为, 有可能出现一些问题.
如果长度不够, 重新开辟一块新的内存将数据复制到其中, 将以前的内存释放.
-
长度够, 还使用以前的内存
int * arr = calloc(10, sizeof(int)); for(int i = 0; i < 10; i++) { arr[i] = (i+1)*10; } realloc(arr, 15);
指针作为函数的返回值, 不能使用局部变量的地址.
-
指向函数的指针声明
- 指针函数的指针, 并不是任意的函数都可以指向
- 而是有限定的, 要求指向的函数的返回值类型和参数描述必须与指针的描述一样.
- 声明语法:
- 返回值类型 (*指针名) ([]参数列表)
void ( * pFun1) (); - int (*pFun2) (int num1, int num2);
- 返回值类型 (*指针名) ([]参数列表)
void test() { } int test2(int a, int b) { } int main(int argc, const char * argv[]) { void (*pFunc)() = test; pFunc(); (*pFunc)(); int (*pTest2)(int a, int b) = test2; pTest2(); }
十三. 结构体
-
如何使用结构体来创建新的数据类型呢?
语法格式:
struct 新类型名称
{
// 在这里面写上, 你创建的类型是由哪些变量联合而成的.
数据类型1 小变量名称1;
数据类型2 小变量名称2;
数据类型3 小变量名称3;
} -
别名:
typedef char* String; typedef struct { char *name; int age; char *sex; } Student; Student student;
十四. 宏定义
它是个预处理指令, 在编译之前执行, 在函数外, #define MAX = 10;
作用: 可以为一段c代码定义一个标识, 如果你要使用这段c代码, 那么你就使用这个标识.
语法: #define 宏名 宏值
原理: 在编译的时候, 就会执行源文件中的预处理指令.会将c代码中, 使用宏名的地方替换为宏值.
将c代码中的宏名替换为宏值的过程叫做 宏替换/宏代换
宏值可以是任意的东西, 定义时, 不会检查语法, 宏替换时, 检查语法
-
如果宏值是一个表达式, 那么宏值并不是表达式的结果, 而是它本身.
#define N = 10+10+20; int main(int argc, const char *argv[]) { int a = 2*N; printf("a = %d\n", a);//50 return 0; }
宏值不可更改
-
宏值可以是表达式,
#define C = a+a; int main(int argc, const char *argv[]) { int a = 10; int c = C; printf("c = %d\n", c); return 0; }
宏可以定义在函数外或者函数内
宏从定义的地方一直到文件结束都可以用, 取消宏undef 宏名,
取消后, 后面就无法使用宏了-
使用宏的注意点:
#define MAX(a, b) a+b; // #define 空格 宏名 空格 宏值
-
static 和 extern
- 修饰成员变量, 在当前模块中使用, 使用static, 跨模块使用extern修饰
- 函数默认使用extern修饰,