一、数据类型
分类:
基本数据类型:(内置:编译器自带的类型):int(4)、short(2)、long(8)、long long、char(1)、float(4)、double(8)
复合数据类型:(多个内置类型的组成的新类型):数组、union、struct、enum
void类型:void *(万能指针):多态
二、定义变量
(注意事项:编码规范)
变量的可读性 形容词_名词 (不允许用拼音命名!!!) int n; int sum_result;
循环变量:int i;int k;
三、基本数据类型需掌握的知识点
3.0 类型转化
C语言类型转换:不安全,可以将任何类型之间转换,有可能造成数据丢失(可以隐式输出,直接编译)
安全的类型转换:先检查两个类型是否可以转换
强制类型转换:(类型名称)变量名
如果一个运算符两边的运算数类型不同,先要将其转换为相同的类型,即较低类型转换为较高类型,然后再参加运算,转换规则如下图所示。
3.1 各种数据类型的字节长度
(变量占用内存的大小:字节)int num = 5;
注:计算机便是内存大小的单位:8bit位 = 1个字节、16bit = 2个字节 = 半字、32bit = 字、双字、kb
1. 数据类型长度
int len = sizeof(num);//sizeof计算数据类型和变量的内存大小
//int len = sizeof(int);
sizeof是一个运算符
int num; int len = sizeof num; //√ int len = sizeof int;//× int len = sizeof(num);//√
2. 指针长度
指针的长度:int *; char *
指针类型:保存地址,操作系统中地址的长度是固定的,是由操作系统位数决定的,64位系统是8个字节、32位系统是4个字节
3. 数组长度
int arry_int[100];//400 char arry_char[100];//100
4. 字符串长度
字符串的长度:int strlen(char *stc) //不统计’\0’
#include <stdio.h> #include <string.h> int main() { char *ptr = "hello world"; printf("sizeof(*ptr) = %ld\n",sizeof(ptr)); printf("strlen(*ptr) = %ld\n",strlen(ptr)); printf("strlen(hello world) = %ld\n",strlen("hello world")); return 0; }
小结:如何测量数据类型的大小
sizeof运算符
sizeof(变量名)
sizeof (数据类型)
strlen 统计字符串中字符的个数
3.2 各种数据类型的取值范围
(计算机是以补码形式保存数据,为解决+0 -0问题)
原码、反码、补码
计算机里保存:补码
正数:原码 = 补码
负数:
补码 = 原码取反 + 1
原码 = 补码取反 + 1
unsigned char:0 - 255(2^8-1)
signed char:-128-127
0 000 0000 = 0
0 111 1111 = 127
1 000 0000 = -128
111 1111
+1 = 2^7 - 1 + 1
1 000 0000 =-128
1 111 1111 = -1
000 0000
+1
1 000 0001 = -1
计算机为什么提出补码存储?
解决 +0 与-0
0 000 0000 = +0
1 000 0000 = -0
printf("%d",~2)
0000 0010
1111 1101
000 0010
+1
1 000 0011 = -3
//例 char ch = -128; ch = ch - 1; //溢出 //计算机不做减法,只做加法 //1 000 0000 //-128 //1 111 1111 //-1 //0 111 1111 = 127//上面两个相加得出结果
//例 char a[1000] for (int i = 0; i < 1000; i++) { a[i] = i - 1; //i = 127 a[127] = -128 //i = 128 a[128] = 127 //i = 128 a[129] = 126 //.. //i = 255 a[255] = 0; ‘\0’ } printf("strlen(a) = %ld\n",strlen(a)); //结果是255 可以好好想想
//例1 char ch =127; ch = ch + 1; printf("%d",ch);// 溢出,输出-128 //例2 char ch =127; printf("%d",ch + 1);//输出128 //例1,是+1后回写到内存中,造成溢出 //例2,是直接输出,所以不存在回写
3.3 无符号和有符号的移植性
signed VS unsigned
有的编译器默认是有符号,有的是无符号
如果不解决,可能导致不同编译器,编译出错
typedef
关键字:给数据类型重命名
typedef unsigned char uChar;
typedef signed char Char;
typedef unsigned int len; //命名易懂
typedef struct student Stu; //结构体重命名,提高编码效率
解决signed、unsigned带来的代码移植性的问题
提高代码的可读性
提高编码效率
四、变量和常量
全局变量和局部变量:字节长度、生命周期、存储区域
作用域:可见范围
局部变量:在函数体里定义的变量–所在函数(出了函数不可见)
全局变量:在函数体外定义的变量–整个全局(需要用外部声明)extern int g_count
生命周期:所在内存空间的分配-释放的过程
局部变量:所在函数体执行时,分配空间,执行结束、释放空间
全局变量:所在程序执行时,分配空间,执行结束,释放空间
存储区域
CPU、硬盘、内存(物理内存,属于稀缺资源)
概念:物理内存与虚拟内存
vim hello.c
gcc hello.c -o hello
存储在硬盘上
CPU读取内存,运算完,回写内存
int num = 5; *(&num) = 10; printf("%p\n",&num); //这个地址在是虚拟内存空间,以便用户不能轻易的访问到物理内存
操作系统会给用户4G的虚拟地址,通过MMU,实现虚拟地址空间(VA)与物理地址空间(PA)的映射
内存4G:1G内核+3G(栈空间、堆空间、数据段、代码段)
栈空间:
存放:局部变量、函数形参、自动变量(少)
特点:先进后出、系统管理(操作系统决定释放与分配)
堆空间:
存放:malloc、ralloc、calloc分配的空间
特点:先进先出、用户管理
数据段
再划分:
bss:未初始化的全局变量
or:常量
静态数据区:static修饰静态变量、初始化的全局变量
初始化未定义,高版本都是0
局部变量:存储在栈空间
全局变量:存储在数据段
五、格式化输出
unsigned int usi_num = 5; signed int si_num = -5; int array[3] = {1,2,3}; char ch = 'a'; char *ptr = "hello world"; char src[100] = "hello"; short s_sum = 6; long l_num = 12345678; float f_num = 1.123; double d_num = 1.324; printf("usi_num = %u\n",usi_num); printf("si_num = %d\n",si_num); for(int i = 0; i < sizeof(array)/sizeof(int); i++) { printf("array[%d] = %d\n",i,array[i]); } printf("ch = %c\n",ch); printf("ptr = %s\n",ptr); while(*ptr != '\0') { printf("%c\n",*ptr); ptr++; } //数组名保存数组首元素地址 printf("src = %s\n",src); printf("s_sum = %d\n",s_sum); printf("l_sum = %ld\n",l_sum); printf("d_sum = %lf\n",d_sum);
printf注意点
printf:行缓冲:满一行(4096)或者遇到’\n’或者被强行输出时,数据才会被输出
printf语句后面要加上\n
printf("hello world!"); while(1);//不输出
printf使用技巧
printf使用技巧:C语言printf()函数中一些不为人知的技巧!
格式化输入
scanf格式化才能输入
注意点
不加\n
加&
指针类型不兼容(%hd、%ld)
跳出数组时,加上\0
getchar()处理垃圾
数据溢出,很危险
遇到空格就停止
gets();获取句子,无缓冲
附
计算机不做减法,只做加法
在c语言中,用typedef重命名后,原来的变量名还可以用
MMU的解释:内存管理单元MMU
定义只能定义一次,而声明可以声明很多次。先声明,再使用
指针需要给其地址
疑问及其解决
疑问1::printf("%d",~2)为什么不直接是正数 1111 1101
解决1:
默认确实是有符号型,所以要看符号位
疑问点2:
unsigned char和unsigned int一样吗?为什么都是输出-3
解决2:
char是8个bit=1个字节
int是32个bit=4个字节(VC中是4个字节、ANSI标准定义中是2个字节)
关于bit等概念,数据范围看:
32位操作系统int类型最大值是多少?
疑问3
大数问题
利用数组连续性,将大数每一位上的数字单独取出放入对应的数组单元中,然后再对每一位做单独的加减乘运算,期间需处理好进位和借位以及其它一些细节性的问题。
疑问4
typedef VS 宏
typedef与#define的区别
typedef只是为了增加可读性而为标识符另起的新名称(仅仅只是个别名),而#define原本在C中是为了定义常量,到了C++,const、enum、inline的出现使它也渐渐成为了起别名的工具。有时很容易搞不清楚与typedef两者到底该用哪个好,如#define INT int这样的语句,用typedef一样可以完成,用哪个好呢?我主张用typedef,因为在早期的许多C编译器中这条语句是非法的,只是现今的编译器又做了扩充。为了尽可能地兼容,一般都遵循#define定义“可读”的常量以及一些宏语句的任务,而typedef则常用来定义关键字、冗长的类型的别名。
宏定义只是简单的字符串代换(原地扩展),而typedef则不是原地扩展,它的新名字具有一定的封装性,以致于新命名的标识符具有更易定义变量的功能。请看:
typedef (int*) pINT;
以及下面这行:
#define pINT2 int*
效果相同?实则不同!实践中见差别:pINT a,b;的效果同int *a; int *b;表示定义了两个整型指针变量。而pINT2 a,b;的效果同int *a, b;表示定义了一个整型指针变量a和整型变量b。
转自:typedef与宏定义区别
编程实训
题目:实现一个计算器,功能加减乘除
#include <stdio.h> int main() { int num_1,num_2,result; char run_num; printf("输入需要运算的两个数,并用逗号隔开:\n"); scanf("%d,%d",&num_1,&num_2); printf("输入运算符,如*,/,+,-\n"); getchar(); scanf("%c",&run_num); switch(run_num) { case '+': { result = num_1 + num_2; break; } case '-': { result = num_1 - num_2; break; } case '*': { result = num_1 * num_2; break; } case '/': { result = num_1 / num_2; break; } default: { printf("error!"); } } printf("%d %c %d = %d",num_1,run_num,num_2,result); return 0; }
作业
1.设定输入长度
2.密码保护