.1、在 C 语言中,sizeof() 是一个判断数据类型或者表达式长度的运算符。返回一个变量或者类型的大小(以字节为单位);例如:sizeof(int i);返回一个4,即为int类型的字节大小;
- char类型定义的变量大小为1个字节长度
short类型 为2个字节大小
int类型为4个字节大小
3、Typedef int INT32
Typedef struct tag_ts //结构体定义
{
INT32 B1;
INT32 B2;
}TS;
TS为两个INT32 即为8个字节
4、在计算机中,正负数表示:
最高位为1,表示为负数
最高位为0,表示为正数
例:
int sign = 0;
char i = -5;
short j = 5;
int k = 6;
sign = ( i & 0x80); //sign 结果不等于0,i表示二进制 10001001 最高位1表示为i的值为负数,与10000000“与”的结果为10000000 不为0;
Sign = ( j & 0x8000); // 根据i、j 、k不同的字节数据类型应选用不用字节大小。
Sign = (k & 0x80000000); //
- 计算机内部用补码表示有符号数:
正数的补码为正数本身;
负数的补码为负数的绝对值取反后加一;
例如:
5:0x00001001 ;-5:0x10001001 取绝对值得0x00001001 取反得0x11110110 再加一得0x11110111;
①、当无符号类型unsigned int i ,与有符号类型int j进行运算时,j经过取绝对值、取反后,会被看作一个很大很大的无符号类型;
即当有符号数与无符号数进行运算时,有符号数会被看作是无符号数进行运算;!!
例如:
unsigned int i=5;
int j=-5;
i+j>0;
而且j是一个非常非常大的一个值
5: 0x1000000000001001 取绝对值0x0000000000001001,取反得0x1111111111110110
0x1111111111110110为j的最终值!!
②、当定义一个unsigned int 类型i时,当i进行i--;操作时,i减到0后,如果再i--;i的值将会变为short类型两个字节的最大值了,即为0x11111110;
7、小数用二进制表示:
乘二取整:
例如:
0.25 化二进制 0.25x2=0.5 0.5x2=1 即为0.5d=0.1b (d表示十进制数,b表示二进制数);
再如:0.123 化二进制 0.123x2=0.246;0.246x2=0.492;0.492x2=0.984;0.984x2=1.968;
......................一直循环下去 最终得到的二进制为0.00011............b 在乘了四次2得到大于1的数,所以之前的位都是0;
- int类型的范围【-2的31次方~2的31次方】;
float类型的范围【-3.4x10的38次方~3.4x10的38次方】
Int和float都占4个字节内存,float比int范围大
原因:
float能表示的具体数字个数与int相同;
float可表示的数字之间不是连续的,存在间隙;
float只是一种近似表示法,不能作为精准数使用;
由于内存表示法相对复杂,(float二进制表示需要含符号位、指数、小数。Int只有符号位和指数。)float的运算速度比int慢
例如:
float f=3.1415f;
float f1=123456789;
printf(“%0.10f\n”,f);//打印出来小数点后十位数据%0.10f即为打印小数点后十位数据;
printf(“%0.10f\n”,f1);//打印出来小数点后十位数据%0.10f即为打印小数点后十位数据;
打印出来的
f=3.1414999962
f1=123546792.0000000000
打印出来的数据并不是实际的值,这就是为什么float不能做精准数使用;
- 强制类型转换:
目标类型能容纳目标值时,结果不变;
目标类型不能容纳目标值时,结果将发生截断。将高位截断,留下地位的数据;
自定义的结构体类型无法转化强制转化为基本类型;
- 变量属性:
auto关键字:它是C语言中局部变量的默认属性,即为所有变量都默认局部变量为auto属性 例如:
Void b(void )
{
Int i; //局部变量默认属性为auto
auto int j;//显示声明auto属性
}
register关键字:指明将局部变量储存于寄存器中,即为定义一个寄存器变量。且不能定义成全局变量,因为如果定义成全局变量,这个寄存器会在程序从开始运行到结束一直被占用,会导致CPU运行出现问题。且只能用于请求寄存器变量,但不一定成功。
不能用&运算符获得register的变量地址,因为&运算符获得的是内存地址,不是寄存器地址;
- static关键字:修饰局部变量,使用static定义局部变量时,将局部变量存储在程序静态区中,(一般定义局部变量是在栈上分配内存空间)
static定义全局变量时,所定义的全局变量只能在当前文件中被访问,不能在其他文件中使用;
例如1:
#include<stdio.h>
Int i ; //普通全局变量
static int j; //静态全局变量,只有当前文件夹能够访问
Int man()
{
Int k; //局部变量,在栈上分配空间
static int t; //静态局部变量,在静态数据区分配空间
}
例如2:
#include <stdio.h>
int f1()
{
int r = 0;
r++;
return r;
}
int f2()
{
static int r = 0;
r++;
return r;
}
int main()
{
for(i=0; i<5; i++)
{
printf("%d\n", f1());
}
for(i=0; i<5; i++)
{
printf("%d\n", f2());
}
return 0;
}
输出结果为:
F1():1、1、1、1、1
F2();1、2、3、4、5
原因:main()每次调用f1()时,r都被初始化为0,然后r++,为1
static定义的静态局部变量r,被存储在静态区中,main()在调用f2()时,只会对r进行一次的初始化为0的操作!;
- extern关键字:用于声明外部定义的变量或函数
也就是说,当看到一个被定义的变量前面有extern ,说明这个变量已经在其他地方定义好了。
Extern “C”
{
...........// 这种格式为C++的格式,是为了让编译器知道 这个大括号里是C语言的编程格式
}
12、if判断语句中。bool类型的可以写成if( bool)进行判断,因为bool类型的变量只有ture和fase两种值,如果if()中是判断是否为0,需要将0写在左边。例如:
if(0==i)
在判断语句中,float型数据不能直接与0值进行比较,需要定义一个接近于0值的数据,用于定位0的精度。例如:
#define a 0.000000001 //用于定位f的精度
float f = 0;
if((-a<=f)&&( f<=a)) // float型判断是否为0的判断语句
{
/
}
`{
/
}
13、break表示终止循环的执行
continue表示终止本次循环,进去下次循环执行
14、存在void类型的指针。
void*指针作为左值用于“接收”任意类型的指针
例如:int* t;
void* p;
p=t; //int*类型赋值给void*类型
t=p; //error 不能直接赋值给其他类型
void*指针作为右值使用时需要进行强制类型转换
15、const修饰的变量是只读的,本质还是变量。
修饰的变量只能出现赋值符号的左边,只能在右边。 它被存储在只读存储区。
当const定义一个值的时候,这个值存储在栈中。虽然是“常量”,其实可以通过改变在栈中分配空间地址里面的值进行修改的。例如:
void f(void)
{
const int c = 1; //const定义一个常量
//c = 5; //error 不能出现在赋值符号的左边。
int* p = (int*)&c; //通过指针将p指向c的地址
*p = 3; // 通过改变指针p的值,来改变p指向的地址的值!!
}
上面定义的为局部变量!!
定义为全局变量回出错。
const不能真正定义上的常量。
int const i;和const int i;一样,没有区别。
16、1.结构体与柔性数组
struct softarray
{
int len; //数组大小
int array[];//柔性数组,是一个大小未知的数组。
} //array仅是一个待定使用的标号符,不占用空间。
Struct softarray* sa = NULL; //定义一个sa的堆空间,NULL意思为空的意思,和’\0’一样
Sa = (struct softarrary*)malloc(sizeof(struct softarray)+ sizeof(int)* 5)//申请5
个array内存空间
Sa-> len = 5;//5个int类型的array[5]数组
例如:
#include <stdio.h>
#include <malloc.h>
struct SoftArray
{
int len;
int array[];
};
struct SoftArray* create_soft_array(int size)
{
struct SoftArray* ret = NULL;
if( size > 0 )
{
ret = (struct SoftArray*)malloc(sizeof(struct SoftArray) + sizeof(int) * size);
ret->len = size;
}
return ret;
}
void delete_soft_array(struct SoftArray* sa)
{
free(sa);
}
void func(struct SoftArray* sa)
{
int i = 0;
if( NULL != sa )
{
for(i=0; i<sa->len; i++)
{
sa->array[i] = i + 1;
}
}
}
int main()
{
int i = 0;
struct SoftArray* sa = create_soft_array(10); //定义柔性数组大小
func(sa); //赋值操作
for(i=0; i<sa->len; i++) //打印数据
{
printf("%d\n", sa->array[i]);
}
delete_soft_array(sa); //清除柔性数组
return 0;
}
- 在一条程序后面加“\”的意思为:此行程序并没有结束,换行的目的是为了方便写程序和阅读。“\”称为接续符 ,
- #if #endif的用法
#if 0
Code
#endif
这里#if后面的0 代表code这里的代码被屏蔽掉了,类似于 //code和 /*code */
将0改为1,则code这行代码被激活。
这种用法一般在做调试经常用到;
本文主要记录了C/C++预处理指令,常见的预处理指令如下:
#空指令,无任何效果
#include包含一个源代码文件
#define定义宏
#undef取消已定义的宏
#if如果给定条件为真,则编译下面代码
#ifdef如果宏已经定义,则编译下面代码
#ifndef如果宏没有定义,则编译下面代码
#elif如果前面的#if给定条件不为真,当前条件为真,则编译下面代码
#endif结束一个#if……#else条件编译块
#error停止编译并显示错误信息
- C语言中的单引号‘’和双引号“”,例如:
‘a’ 表示字符字面量,在内存中占1个字节,‘a’+1表示a的ASCII码加一,结果为b
“a”表示字符串字面量,在内存中占两个字节,一个是字符串自己占一个字节,还有一个结束符“\0”占一个字节。一个占两个字节。
20、C语言运算符逻辑与&& 比 逻辑或|| 的优先级高,应该运算逻辑与&&。
例如: ++i || ++j && ++k;
先进行++j && ++k的运算,再进行++i的运算,即为++i && 1; 这里为或运算,右边已经为1,结果肯定为1,此时++i不进行运算直接输出,所以i的值为0;
以上结果输出是错误的!!
知识点:
程序中的的短路:
|| 从左向右开始运算,,当遇到为真的条件时停止运算,整个表达式为真,所有的条件为假是表达式才为假;
&& 从左向右运算,当遇到为假的条件时停止运算,整个表达式为假,所有条件为真时才为真;
上面的运算顺序 (++i)|| (+++j &&++k) 先进行++i的运算,此时i=1;条件已经为真,停止运算,所以j和k的值为0;最终的结果为 i=1,k=0,j=0;
21、位移运算 >> 、<< 在计算机中执行效率高于数学运算符运算,所以在程序编写过程中尽量用位移运算代替数学运算;
左移n位相当于乘以2的n次方, 右移n位相当于除以2的n次方;
运算优先级:四则运算>位运算>逻辑运算;
22、 指针
(1)、printf输出指针使用
printf(" point = %p \n ",point );
(2)、 禁止不同类型的指针相互赋值;
禁止将普通数值当作地址赋值给指针;
(3)、 数组和指针
数组名可看作一个指针,数组名的地址,为第一个元素的地址
int a[4] = {0,1,2,3};
int* p = a; //p为数组名地址,也为第一个元素地址 <=> a=&a[1];
p++; //代表第二个元素1,<=> *p= 1;
(4)、&a :取地址操作,即为a的地址;
a为非指针数据,在对其进行地址操作时需要进行取地址,然后通过*a 来操作a的值
例如:
int a;
void test(int* b)
{
*b++;
}
test(&a); //先取a的地址,然后在函数中 使用*a来对a的值进行操作。
(5):定义初始化指针数组:
int a[4] = {0,2,3,4};
int(*pName)[4] = &a; //定义pName数组指针,用a数组的地址进行初始化使其初始化为a[4],a[4] = {0,1,2,3};
类似于:
int b;
int* p = &b; //对指针p初始化操作。
printf(" %p",p);//打印指针类型
指针操作:
int a[3] = {1,2,3};
int* p = a;
int v = *p++; //此处v值 为a[1] 原因是 v = *p++中 先操作的是*p 把*p的值给v,然后再p++!!!!
当p指向数组元素的时候才能进行指针运算。
(6)、 结构体中指针的使用
/*定义一个结构体*/
typedef struct
{
u8 a;
u8 b;
u8 c;
}POINTER;
POINTER pointer;
void time(u8* pointer) //定义一个函数,输入参数为指针
{
pointer->a = 0x23; // ok! 这里的pointer->a <=> (*pointer).a 单独的pointer为一个指针地址,
pointer.a = 0x23; //error! 此处的pointer为指针地址,
pointer->b = 0x34;
}
(7)、 函数指针
函数指针只是单纯的保存函数的入口地址,因此,只能通过函数指针调用目标函数,不能进行指针移动(指针运算)
int add(int a , int b)
{
return a+b;
}
int nul(int a ,int b )
{
return a*b;
}
int calculate(int a[],int len,int(*cal)(int,int)) //
{
int ret =a[0];
int i=0;
for(i=1;i<len;i++)
{
ret=cal(int,a[i]);
}
return ret;
}
main()
{
int t;
int a[] = {1,2,3,4,5};
int(*pFunc)(int,int) = null;//初始化一个函数指针
pFunc = add; //即为pFunc函数指向add函数,pFunc调用add函数
t=pFunc(1,3);//t的结果为1+3;
t = calculate(a,5,add); //t的值为:1+2+.....+5;
//calculate中调用add函数。
}
(7) 指针打印
int main()
{
//int ;
int a =10,*p;
p=&a;
printf("指针p的地址:%p\n",p); //打印p地址
printf("变量a的地址:%p\n",&a); //打印a的地址
printf("指针p的值:%d\n",*p); //打印指针p 的取值
printf("变量a的值:%d\n",a); //打印a的值
}
23、指针与堆空间
全局数据区:存放全局变量,静态变量
栈空间:存放函数参数,局部变量
堆空间:用于动态创建变量(数组)
void * 类型的指针无法访问内存的数据
例如:printf("%f\n",*p); //原因是void* 指针类型是空的,且没有长度,所有无法访问。
申请、释放内存
头文件 #include<stdlib>
int p = malloc(4*sizeof(int)); //申请4个int类型的内存空间,即为4*4个字节内存
free(p); //释放内存,只能释放申请到的内存,且不能多次释放。
申请的p指针内存 通过printf("%d\n",*p); 打印出来的一个不固定的数值。
if(p=NULL); //使用前需要判断下p是否为NULL, 当p=NULL时,说明p没有可用的内存了。
多级指针:
type v;
type* pv=&v;
type** ppv = &pv; //ppv 指向一级指针pv type* 类型的指针,type* 为pv类型指针 ppv为一个二级指针。
type*** pppv = &ppv; //三级指针
int a= 0;
int* p=&a;
int **pp=&p;
**pp=2; //*pp 等价于p,*(*pp)等价于*p,*p=&a,所以此处**pp 等价于对a=2;
*pp=&b; //等价于p =&b;
*p=3; //等价于b=3;
int main()
{
int b[][2] = {{1, 2}, {3, 4}};
int (*pnb) [2] = b; // b 的类型是 int(*)[2] => pnb[0]->[0,1];pnb[1]->[3,4];
*pnb[1] = 30; //*(pnb[1])-> [3,4]中的3值,这里30替换3的值输出。
printf("b[0][0]%d\n",b[0][0]); // 输出的值为1
printf("b[0][1]%d\n",b[0][1]); // 输出的值为2
printf("b[1][0]%d\n",b[1][0]); // 输出的值为3
printf("b[1][1]%d\n",b[1][1]); // 输出的值为4
}
野指针:野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)指针变
量在定义时如果未初始化,其值是随机的,指针变量的值是别的变量的地址,意味着指针指向了
一个地址是不确定的变量,此时去解引用就是去访问了一个不确定的地址,所以结果是不可知的
例如:
int* fun()
{
int var = 100;
return &var;
}
int *p = fun(); //这里将var的地址返回给了*p 此时的var已经被fun()函数返回,
//局部变量和参数var已经被销毁了var的地址已经没有变量存在,
//此时的p已经是一个野指针了。*p= 200;这里又给其赋值,
//是不合法的。所以不要从函数中返回局部变量/函数参数的地址
*p = 200; // 改变 func 函数中局部变量 var 的值,这里不合法。
24、自定义数据类型
语法:typedef Type NewTypeName;
例如:
typedef unsigned char bype;
bype b = 128;
stuct 定义不同数据类型变量的集合类型
语法:
struct TypeName
{
type1 var;
type2 var2;
;;//
typeN varN;
}
例如:
struct student
{
char name[20];
int id;
short major;
}
int main()
{
strcat student s1 = {"ming",155,2};
printf("studentName:",si.name);
printf("studentid:",si.id);
printf("studentmajor:",si.major);
}
深入struct 结构体类型
(1)、 struct 结构体变量本质是变量的集合;
(2)、 struct 结构体变量中的成员占用的独立的内存;
(3)、 struct 结构体可用typedef赋予新类型名;
例如:
typedef struct student stu; //给结构体赋予新的名字。
struct student
{
char name[];
int di;
short major;
}
(4)、 可 struct 结构体类型的指针,并执行对应类型的变量
/*******************进阶知识学习********************/
25、 #运算符
(1)、 #运算符用于在预处理期将宏定义转换为字符串
(2)、 #的转换作用是在预处理期完成的,因此只在宏定义中有效
(3)、 编译器不知道#的转换作用
用法:
#define STRNG(x) #x //STING 为任意定义。
printf("%s\n",STING(Hello World));
例子:
#define CALL(f, p) (printf("Call function %s\n", #f), f(p))
int square(int n,)
{
return n * n;
}
int func(int x)
{
return x;
}
int main()
{
int result = 0;
result = CALL(square, 4);
//result =(printf("Call funtion %s \n",#spuare),spuare(4));
printf("result = %d\n", result);
result = CALL(func, 10);
//result = (printf("Call funtion %s\n",func),func(10));
printf("result = %d\n", result);
return 0;
}
26、 指针的本质 是变量
*的意义:
(1)、*表示所声明的变量为指针
(2)、*表示取指针所指向的内存空间中的值
例如:
int i = 0;
int j = 0;
int *p = &i;//这里将i的地址给指针p p保存的为i的地址,p本身又存在它自身的地址
// *p为i
*p = 10; //这里是给p指针所在的地址赋值,p指针的地址为i的地址,所以本质是给i赋值。
不同类型的指针 占用内存一样
指针大小与系统有关系 32位系统中,0xffffffff 指针地址为4个字节大小
所以所有的指针类型占用内存大小为4个字节
27、指针和数组的分析:
数组的本质就是一段连续的内存空间
数组运算:
int a[5]={0};
a+1==> 数组a首地址+sizeof(*a)=>a的首地址+数组a类型的大小
例如:a[0]:0x00001001
a+1==> 0x00001001 + 1*sizeof(*a)==>0x00001001 + 1*sizeof(a[0])==>0x00001001+1*4=0x00001101
指针运算:
指针运算公式:p + n; ==> (unsigned int)p+n*sizeof(*p);
//指针在系统中占用的内存大小(4)+ n(要加的数)sizeof(*p):指针p指向的数据类型内存大小
指针之间运算 :只支持减法运算
参与减法运算的指针类型必须相同
公式:p1-p2 <==>((unsigned int)p1-(unsigned int)p2)/sizeof(type);
注意:
(1)、只有当两个指针指向同一个数组中的元素时,指针相减才有意义,其意义为指针所指元素的下标差
(2)、当来两个指针指向的元素不在同一数组时,结果未定义。
C语言实现时间输入:
scanf("%d",a);
b=a/3600; //小时
c=a%3600/60; //分钟
d=a%3600%60; //秒