C语言许·学习笔记

.1、在 C 语言中,sizeof() 是一个判断数据类型或者表达式长度的运算符。返回一个变量或者类型的大小(以字节为单位);例如:sizeof(int i);返回一个4,即为int类型的字节大小;

  1. char类型定义的变量大小为1个字节长度

short类型 为2个字节大小

int类型为4个字节大小

3Typedef 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); //

  1. 计算机内部用补码表示有符号数:

正数的补码为正数本身;

负数的补码为负数的绝对值取反后加一;

例如:

 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;

  1. 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不能做精准数使用;

  1. 强制类型转换:

  目标类型能容纳目标值时,结果不变;

  目标类型不能容纳目标值时,结果将发生截断。将高位截断,留下地位的数据;

自定义的结构体类型无法转化强制转化为基本类型;

  1. 变量属性:

auto关键字:它是C语言中局部变量的默认属性,即为所有变量都默认局部变量为auto属性 例如:

Void b(void )

{

Int i;    //局部变量默认属性为auto

auto int j;//显示声明auto属性

}

register关键字:指明将局部变量储存于寄存器中,即为定义一个寄存器变量。且不能定义成全局变量,因为如果定义成全局变量,这个寄存器会在程序从开始运行到结束一直被占用,会导致CPU运行出现问题。且只能用于请求寄存器变量,但不一定成功。

不能用&运算符获得register的变量地址,因为&运算符获得的是内存地址,不是寄存器地址;

  1. 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的操作!;

  1. 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;

}

  1. 在一条程序后面加“\”的意思为:此行程序并没有结束,换行的目的是为了方便写程序和阅读。“\”称为接续符 ,
  2. #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停止编译并显示错误信息

  1. 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; //秒
    

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值