C语言基础

C语言基础

 

原码、反码、补码(只跟负数相关)

  • 正数的原码、反码、补码都相同(也就是说原码、反码、补码只跟负数相关)
  • 原码:把数字转换成二进制
  • 反码:除最高位,其他位依次取反
  • 补码:反码+1

注意:计算机中使用补码来表示数据的!!!(由于正数的原码、反码、补码相同,所以在计算机中显示的二进制(补码)和我们通过计算把数字转码成二进制(原码)相同)


java基本数据类型

  • byte:1
  • short:2
  • int:4
  • long:8
  • float:4
  • double:8
  • char:2
  • boolean:1

C基本数据类型(可通过sizeof函数查看大小)

  • short:2
  • int:4
  • long:4
  • float:4
  • double:8
  • char:1
  • 注:long long 占8个字节,但是并不属于基本数据类型

有符号(signed)、无符号(unsigned)

  • int类型,占4个字节(一个字节由八个二进制组成),32位
  • int类型能表示 2^32 个数字
  • 无符号int类型,取值范围是:0 ~ (2^32 - 1)
    • 所有32位都表示数值位
  • 有符号int类型,取值范围是:-2^31 ~ (2^31 - 1)
    • 最高位为符号位,剩下31位作为数值位
    • 当最高位为0时,剩下的31位都表示一个正数
    • 当最高位为1时,剩下的31位都表示一个负数
  • 所以int能表示的最大值是 (2^32 -1)
  • signed:有符号,最高位表示符号位
  • unsigned:无符号,最高位表示数值位

在c语言中没有字符串,只能使用字符数组来表示字符串;每个字符数组最后面都有一个结束符\0,所以字符数组的长度都+1;

char c[] = "你好啊hello";
  • 1
  • 2

输入输出函数

%d  -  int  
%ld – long int  
%lld - long long  
%hd – 短整型  
%c  - char  
%f -  float  
%lf – double  
%u – 无符号数  
%x – 十六进制输出 int 或者long int 或者short int  
%o -  八进制输出  
%s – 字符串  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • c语言中数组不检测越界

指针

内存地址

  • 是用十六进制的数字来表示内存地址的
  • 内存中的每一个字节都会有一个地址,地址相当于内存的门牌号
  • 32位系统的地址总线长度是32位的,也就是说系统能分配给内存地址的数量是 2的32次方 个

    为什么32位系统最大只支持4G内存?
    因为:系统最多能分配给内存地址的数量是:2^32 个,而每个地址对应一个字节,也就是总共 2^32 b(字节),2^32/1024/1024/1024 = 4G

  • 内存修改器:找到变量所在的地址,然后修改地址上的值

指针变量是用来存储地址的

  1. 一级指针

    • int* p;// 定义一个 int* 型的指针变量,这个变量只能保存地址,而且保存的地址上存放的数据必须是一个int型(因为:如果你存放其他数据类型,那么你取那个指针存放的的值时,会取不到值)
    • int i = 3;
    • p = &i;// 将整数3的地址存放到指针p中
    • *p:引用这个指针所指向的地址上保存的数据
  2. 二级指针

    • int** q;// 定义一个二级指针,二级指针保存的是一级指针的地址
  3. 指针如果没有赋值,那么就不能使用,此时指针指向的地址是一个随机值,这种指针叫野指针

值传递和引用传递

  • 引用传递,本质上也是值传递,只不过传递的这个值是一个地址值

一个函数返回多个值

  • 由于c语言是面向过程的,java是面向对象的,在java中,如果想要让一个函数返回多个值的话,可以将函数中要返回的数据封装成对象,然后就可以返回多个值了。
  • 而在c语言中,一个函数只能返回一个值,

        #include<stdio.h>
        #include<stdlib.h>
        void function(int* p,int* q){
            *p += 5;
            *q += 5;
        }
        main(){
            int i = 3;
            int j = 5;
            function(&i,&j);

            printf("%d\n",i);// 8 直接修改内存地址保存的数据值
            printf("%d\n",j);// 10

            system("pause");
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

子函数获取主函数变量的地址

#include<stdio.h>
    #include<stdlib.h> 

    void function(int* p){
         printf("%#x\n",p);// 0x28ff44
    }

    main(){
        int i = 3;
        function(&i);
        printf("%#x\n",&i);// 0x28ff44
        system("pause");
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

主函数获取子函数变量的地址

#include<stdio.h>
    #include<stdlib.h> 

    void function(int** p){
         int i = 3;
         *p = &i;
         printf("i的地址为%#x\n",&i);// 0x28ff44
    }

    main(){
        int* mainp;
        function(&mainp);
        printf("主函数获取到i的地址为:%#x\n",mainp);// 0x28ff14,这里打印的是mainp的值,而mainp是一个指针,所以它的值是一个地址
        printf("主函数获取到i的值为:%d\n",*mainp);//这里获取不到正确的值,因为函数中的变量,在函数结束之后会被销毁。但是当注释掉前面两个打印语句之后,可以打印出3这个值,这是因为那个函数中的变量还没来得及销毁,所以你取出的值是一个"幻值",这种做法是不正确的
        system("pause");
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • int i = 3; 那么i的值就是3;int* p = &i; p的值是一个地址值(i的地址)


数组

  • 数组每一个元素的地址都是连续的,数组是一个连续的内存空间
  • 数组名保存的其实是第0个元素的地址
  • 通过首地址的运算,就可以拿到每一个数组中每一个元素

指针的运算

    int i[] ={1,2,3};
    int* p = &i;
    *P   // 1
    *(p+1) // 2
    char* q = &i;
    *q // 1
    *(q+4) // 2  注意:这里如果写成*(q+1)是错误的!!!因为数组中存储的是int型,每个元素占4个字节,指针应该向右位移4个字节才能拿到下一个元素
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • +1表示向右位移一个单位,一个单位是多少个字节,看指针类型而定;如果指针式int型的,那么位移一个单位是4个字节,而如果指针类型是char型的,那么位移一个单位是1个字节!

指针的长度

  • 32位操作系统 指针长度4个字节,跟类型无关
  • 64位操作系统 指针长度8个字节,跟类型无关
  • int* p和double* q的指针长度相同

堆和栈的区别:

(1) 在栈上分配的内存,叫静态内存
(2) 在堆上分配的内存,叫动态内存

区别:

  1. 申请方式

    • 栈: 由系统自动分配,后进先出,由系统释放这个空间
    • 堆: 需要程序员自己申请,并指明大小,在c中用malloc函数
      如char* p1 = (char*) malloc(10); //14byte (虽然程序员申请了10个字节,但是由于p1是一个指针,指针的长度是4个字节,也就是占用4个字节,指针是保存在栈内存中,所以那句语句总共占用14个字节的空间)
      但是注意p1本身是在栈中的
  2. 系统申请后的反应

    • 栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。
    • 堆:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样,代码中的delete语句才能正确的释放本内存空间。另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中。
  3. 申请大小的限制

    • 栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。栈顶的地址和栈的最大容量是系统预先规定好的,在WINDOWS下,栈的大小是2M,如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。

    • 堆:堆是向高地址扩展的数据结构,是不连续的内存区域。大小跟硬件有关

  4. 申请效率的比较

    • 栈:由系统自动分配,速度较快。但程序员是无法控制的。
    • 堆:由malloc/new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便.
  5. 堆和栈中的存储内容

    • 栈:函数的各个参数、局部变量等。注意静态变量是不入栈的。
    • 堆:堆中的具体内容有程序员安排。
  6. 内存的回收

    • 栈上分配的内存,编译器会自动收回
    • 堆上分配的内存,要通过free来显式地收回,否则会造成内存泄漏。

堆内存申请

  • 使用malloc函数申请堆内存
    int* p = malloc(4 * 10);// 申请了40个字节大小的空间,并且申请的这40个空间是连续的,返回的是堆内存地址(用一个指针p接收)

    释放申请的堆内存
    free(p);

realloc函数的使用

#include<stdio.h>    
    #include<stdlib.h>      

     main(){
     printf("请输入班级人数:");
     int count;
     scanf("%d", &count);

     int* p = malloc(count * sizeof(int));

     int i;
     for(i = 1; i <= count; i++){
           printf("请输入第%d个学生的学号:", i);
           scanf("%d", p + i - 1);
     }

     printf("请输入插班人数:");
     int newCount;
     scanf("%d", &newCount);

     //现在原来旧的堆内存后面,扩展新的空间 
     //如果旧堆内存后面的空间已经被别的程序占用了,那么就无法扩展 
     //如果不能扩展,就会寻找一个块足够大的内存区域,申请新的堆内存 ,并且会把旧堆内存的数据复制到新的堆内存中 
     //释放旧的堆内存 
     p = realloc(p , (count + newCount) * sizeof(int));

     for(i = count + 1; i <= count + newCount; i++){
           printf("请输入第%d个学生的学号:", i);
           scanf("%d", p + i - 1);
     }    

     for(i = 1; i <= count + newCount; i++){
           printf("第%d个学生的学号为%d\n", i, *(p + i - 1));      
     }

      system("pause");
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

结构体

  • struce 关键字 定义结构体
    第一种
struct Student
{
  int age;
  float score;
  char sex;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

第二种

struct Student2
{
 int age;
 float score;
 char sex;
} st2;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

第三种

struct
{
 int age;
 float score;
 char sex;
} st3
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 结构体中不能定义函数,但是可以定义函数指针
  • 函数也是保存在栈内存中,就像定义变量一样
  • 结构体长度不固定,具体长度通过sizeof()得到
    #include <stdio.h>
        #include <stdlib.h>

        void study(){
             printf("天天打怪兽\n");    
        } 

        // 定义结构体 
        // 结构体中不能定义函数,但是可以定义函数指针 
        struct student{
              int age;
              int height;
              char sex;       
                      void(*studyp)();//studyp 函数指针名 
        };
        main(){
              struct student std = {20,180,'f',study};// std 结构体变量
              printf("芳龄%d\n",std.age); 
              std.studyp();// 第一种方法 
              struct student* stdp = &std;
              (*stdp).studyp(); // 第二种方法 
              // ->左边必须是结构体的一级指针,右边是函数指针名字 
              stdp->studyp();// 第三种方法(开发中一般使用这种 ) 

              system("pause");
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

联合体

  • 联合体作用就是:联合体变量可以被赋值联合体定义的任意类型
    #include<stdio.h>
    #include<stdlib.h>
    main(){
        union{int i; char c; short s} un;
        un.i = 30;
        un.s = 20;         
        printf("%d\n",un.i);
       // 20 联合体同时只能有一个值,后面的值会把前面赋给联合体变量的值覆盖掉 
        printf("%d\n",sizeof(un));
        // 4个字节 联合体长度取变量中的最大值 int 
           system("pause");      
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

枚举

#include <stdio.h>

    enum WeekDay
    {
    Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday
    };

    int main(void)
    {
      //int day;
      enum WeekDay day = Sunday;// 只能从定义中的内容取,否则报错 
      printf("%d\n",day); // 6
      system("pause");
      return 0;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

自定义类型

typedef newName oldType;

    #include<stdio.h>
    #include<stdlib.h>

    // hehe这个时候跟int的作用一样,相当于给int起了个别名 
    typedef int hehe;
    main(){
           hehe i = 3;
           printf("%d\n", i);// 3 
           system("pause");
    } 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值