C语言开发要点

创建时间:2018/8/20 13:03:45
关键字:Dev-C++ 内存地址 指针 结构体 联合体

1. C开发

要点:

开发工具:Dev-C++

C 基本数据类型

C 输出、输入函数

内存地址概念

指针入门 多级指针 数值和指针

结构体

联合体

枚举

1.1 HellloWorld

#include<stdio.h>    // .h c的头文件。stdio: standard io 标准输入输出

#include<stdlib.h>   // stdlib: standard library 标准函数库 java.lang等

main(){          

printf("helloworld!\n");  //System.out.println();   "\n"换行符

}

1.2 基本数据类型

C数据类型与Java数据类型对照

各种类型的存储大小与系统位数有关,目前通用的以64位系统为主。

java基本类型        C基本类型

boolean                无(替代:0 false ,1 true)

byte(-128 127)     无

char                      char  1个字节(signed char -128 到 127,unsigned char 0 到 255)

short                     short 2个字节

int                          int  4个字节 -2,147,483,648 -2,147,483,647(unsigned int 0-4,294,967,295)

long                       long  4个字节 -2,147,483,648 -2,147,483,647(unsigned long 0-4,294,967,295)

float                       float  4个字节 1.2E-38 到 3.4E+38  6 位小数

double                  double 8个字节 2.3E-308 到 1.7E+308    15位小数

                            long double 16 字节  3.4E-4932 到 1.1E+4932  19 位小数

void                      void

 

1.3  输出、输入函数

printf("要输出的内容", 变量);

%d  -  int

%ld – long int

%lld - long long

%hd – 短整型

%c  - char

%f -  float

%lf – double

%u – 无符号数

%x – 十六进制输出 int 或者long int 或者short int

%o -  八进制输出

%s – 字符串

24910               1100001 01001110

12345678  10111100 01100001 01001110 */

 

char c='a';

short s = 123;

int i = 12345678;

long l = 1234567890;

float f = 3.1415;

double d = 3.1415926;

printf("c = %c\n", c); //c = a

printf("s = %hd\n", s); //s = 123

printf("i = %hd\n",i); //i = 24910

printf("l = %ld\n",l);  //l = 1234567890

printf("f = %.4f\n",f);  //f = 3.1415默认输出6位有效数字的小数 想手动指定 加上.X

printf("d = %.7lf\n",d); //d = 3.1415926

printf("%#x\n",i); //0xbc614e

printf("%#o\n",i); //057060516

char cArray[]={'a','b','c','d','\0'};

char cArray[]="你好";

printf("cArray = %s",cArray);

system("pause");

 

scanf("占位符",输入内容要存放的内存地址)

main(){    

      printf("请输入班级的人数:");

      int count;

      scanf("%d", &count); //&取地址符

      printf("班级的人数是%d\n",count);

      char cArray[20];//c的数组不检测下标越界

      printf("请输入班级的名字:");

      scanf("%s",&cArray);

      printf("班级的人数是%d,班级的名字%s\n",count,cArray);

      printf("count的地址%d\n",&count);

      printf("cArray的地址%d\n",&cArray);

      system("pause");

 }

1.4 内存

1.4.1 内存地址

内存的最小存储单元是一个字节(Byte),所有存储单元从0开始编号,某一个编号就是对应存储单元的内存地址。

假设内存有8个存储单元,编号为0到7,则需要3位地址总线,原理如下:

内存地址(0到7)使用3位地址总线的状态值表示,则对应的可表示为:000、001、010、011、100、101、110、111

32位操作系统,指的是32地址总线,支持的最大内存为232 Byte,即4G Byte:

232 =2(2+10+10+10)=22*210*210*210 =4G

32位操作系统,地址的表示用2进制是32位,用16进制则是8位,如0x00000001。

64位操作系统,内存地址用2进制是64位,用16进制则是16位,如0x00000000D2400000。系统的硬件也会占用一些内存,实际留给系统的可用存储会小一些,如下图为64位系统下网卡占用内存情况:

Windows下sleep未定义的问题:

引入头文件windows.h

#include <windows.h>

sleep(2000)改为 Sleep(2000);

1.4.2 内存分配

1.静态内存分配:

栈内存 系统统一分配 统一回收。

值只能使用一次,之后内存被回收,可能因为重新使用而值发生变化。

 

2.动态内存分配:

c   malloc memory allocation 内存分配

c的堆内存 程序员手动申请手动释放  malloc free

申请一块堆内存 动态内存分配

堆内存 不连续的 堆内存大小不固定 取决机器的状态

main(){

     //malloc 接收的参数 申请内存大小 返回一个内存地址值 申请到的也是一块连续的内存空间   

      int* pointer = malloc(sizeof(int)*5);

      *(pointer+0) = 1;

      *(pointer+1) = 2;

      *(pointer+2) = 3;

      *(pointer+3) = 4;

      *(pointer+4) = 5;

      //C for 循环 循环的临时变量i 要先声明再使用

      int i;

      for(i = 0;i<5;i++){

              printf("第%d个元素的值= %d\n",i,*(pointer+i));

      }

      free(pointer);

      printf("第一个元素的值%d\n",*(pointer+0)); //这里已经回收 取不到预期的值

       system("pause");

      }

     

// 内存空间不够用,重新申请内存 之前的值会复制到新的对内存中

 int increment;

scanf("%d",&increment);

 pointer = realloc(pointer,sizeof(int)*(count+increment));

 

1.5 指针

1.5.1 取内存地址

取地址符 &

main(){  

  int i = 123;

  printf("%#x\n",&i);

}

1.5.2 指针 存内存地址

int* pointer   // pointer指针,用于保存内存地址(int*表示定义了int类型的指针变量)

int* pointer = &i; // 用取地址符&i 把变量i地址取出来,用指针变量pointer 保存

    *pointer // 通过指针取地址里的值

    *pointer  = 456; // 通过指针修改地址里的值

 

main(){  

       int i = 123;

       printf("i的地址:%#x\n",&i);  // i的地址:0x61ff08

       int* pointer = &i;

       printf("pointer的值 = %#x\n",pointer); // pointer的值 = 0x61ff08

       printf("*pointer的值%d\n",*pointer);  //*pointer的值123

       *pointer = 456;

       printf("i的值是%d\n",i); //i的值是456

       system("pause");

   }

1.5.3 指针使用常见错误

1. 野指针问题

指针定义了,没有使用地址进行赋值,这时修改野指针的地址里的值是不允许的。

指针使用之前要初始化 赋给它一个自己程序中声明的变量的地址。

int* pointer;

printf("pointer的值 = %#x\n",pointer); // 这是一个随机的操作

*pointer = 123; //异常 拿到一个内存地址就改里面的值,系统是不允许的。

2. 类型不一致

   int i;

   double d = 3.1415;  //8个字节

   int* pointer = &d;   //只取出数据 低4位的4个字节

   printf("pointer的值=%#x\n",pointer);

   printf("*pointer = %d\n",*pointer);

附:小端模式(Little-endian), 是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内在的低地址中;

1.5.4 交换两个数的值(值传递/引用传递)

值传递和引用传递传递的实际上 都是数值 只不过引用传递传递的是地址值

如果想通过一个子函数来修改main函数中变量的值 一定要用引用传递

 

/**

* 在该方法作用域内对两个变量进行交换,这两个变量是拷贝的副本(在新的内存地址中),不影响调用者的主函数中变量的值

*/

swap(int i, int j){

         int temp = i;

         i = j;

         j = temp;

}

    

/**

* 将两个地址中的值进行了交换

*/     

swap2(int* pointer, int* pointer2 ){

         int temp = *pointer;

         *pointer = *pointer2;

         *pointer2 = temp;   

}

main(){    

       int i = 123;

       int j = 456;

       // swap(i,j);  // 这样不能交换i j的值,因为方法蚕食使用值传递(拷贝副本)

       //swap2(&i,&j); // 这样可以交换i j的值   

printf("i的值%d,j的值%d\n",i,j);

       system("pause");

}

1.5.5 指针变量的长度

     int* pointer;

      double* pointerD;

      printf("int类型的指针变量占%d个字节\n",sizeof(pointer)); //4个字节

      printf("double类型的指针变量占%d个字节\n",sizeof(pointerD)) //4个字节

 

32位操作系统地址总线是32位,4个字节的变量即可内存地址,指针变量占4个字节

64位操作系统,指针变量占8个字节 ,与什么类型的指针没有关系

1.6 数组

数组实际上就是一块连续的内存空间。

数组变量名的地址实际上是第一个元素的地址.

   // char array[] = {'a','b','c','d','\0'};          // 地址连续

   printf("array[0]的地址%#x\n",&array[0]); //array[0]的地址0x61ff07

   printf("array[1]的地址%#x\n",&array[1]); //array[1]的地址0x61ff08

   printf("array[2]的地址%#x\n",&array[2]); //array[2]的地址0x61ff09

   printf("array[3]的地址%#x\n",&array[3]); //array[3]的地址0x61ff0a

   printf("array的地址%#x\n",&array); //array的地址0x61ff07

char* pointer = &array;

   printf("*(pointer+0)=%c\n",*(pointer+0));   //*(pointer+0)=a

   printf("*(pointer+0)=%c\n",*(pointer+1)); //*(pointer+0)=b

   printf("*(pointer+0)=%c\n",*(pointer+2)); //*(pointer+0)=c

   printf("*(pointer+0)=%c\n",*(pointer+3)); //*(pointer+0)=d

   

   printf("*(pointer+0)=%d\n",*(pointer+0)); //*(pointer+0)=97

   printf("*(pointer+1)=%d\n",*(pointer+1)); //*(pointer+1)=98

   printf("*(pointer+2)=%d\n",*(pointer+2)); //*(pointer+2)=99

   printf("*(pointer+3)=%d\n",*(pointer+3)); //*(pointer+3)=100

 

 

  int array[] = {1,2,3,4};  // 地址差4

printf("array[0]的地址%#x\n",&array[0]);

printf("array[1]的地址%#x\n",&array[1]);

printf("array[2]的地址%#x\n",&array[2]);    

printf("array[3]的地址%#x\n",&array[3]);

  printf("array的地址%#x\n",&array);

 

// char* pointer = &array;  //char*类型指针+1挪动1个字节 不能取到各元素的值,打印的结构是 1 、0 、0 、0

int* pointer =&array;  //+1 挪动4个字节 刚好对应下一个元素地址 能取出各元素的值

   

printf("*(pointer+0)=%d\n",*(pointer+0)); //*(pointer+0)=1

  printf("*(pointer+1)=%d\n",*(pointer+1)); //*(pointer+1)=2

  printf("*(pointer+2)=%d\n",*(pointer+2)); //*(pointer+2)=3

  printf("*(pointer+3)=%d\n",*(pointer+3)); //*(pointer+3)=4

 

1.7 多级指针

二级指针只能保存一级指针的地址。

三级指针只能保存二级指针的地址。

 

int i = 123;

int* pointer1 = &i;

int** pointer2 = &pointer1;  //二级指针

printf("pointer = %d\n",*pointer2); //取pointer1

printf("i = %d\n",**pointer2); //取i 的值

 

int*** pointer3 = &pointer2; //三级指针

printf("pointer2 = %d\n",*pointer3); //取pointer2

printf("pointer1 = %d\n",**pointer3); //取pointer1

printf("i = %d\n",***pointer3);  //取变量 i 的值

 

/**

main函数获取子函数中临时变量的地址 给指针赋值

*/

function(int** pointer2){

   int i = 123;

    *pointer2 = &i;  

    printf("i的地址%#x\n",&i);    

}

 

main(){    

      int* pointer1;

      function(&pointer1);

      printf("pointer1的值%#x\n",pointer1);

       system("pause");

}

 

1.8 结构体

结构体的大小大于等于结构体中每一变量的占字节数的和

  结构体的大小是最大的那个变量所占字节数的整数倍 (内存对齐便于位运算)

  C结构体中不能定义函数  

 

void study(){

    printf("good good study!\n");

 }

typedef struct Student{

      int age;  //8

      int score;  // 4

      char sex;   //1

//函数指针

      void(*studypointer)();函数指针的定义   返回值(*函数指针变量名字)(返回值);

} stud;

main(){    

 stud stu = {18,100,'f'};

 stu.studypointer = &study;

 stu.studypointer(); //通过函数指针 调用函数

 

 struct Student* stuPointer = &stu;

 printf("*stuPointer.age = %d\n",(*stuPointer).age);

 (*stuPointer).sex ='m';

 

 printf("stu.sex = %c\n",stu.sex);

 printf("stuPointer->age = %d",stuPointer->age); //结构体指针使用间接引用运算符访问成员

 system("pause");

}

 

函数指针的定义:

void fun()  

{  

    printf("调用成功\n");  

}  

int main()  

{  

    void(*pfun)() = &fun;  

    (*pfun)();  

    system("pause");  

    return 0;  

 

1.9 联合体

共用体 嵌入式设备上起到节省内存空间的作用

union u{

  int num;  //4个字节

  double d;  //8个字节

}

main(){

   union u u1;

   printf(“union占%d个字节\n”,sizeof(u1)); //8个字节

   printf("u1.num = %d\n",u1.num);

}

 

1.10 枚举

规定取值范围

不手动指定 从0开始,后面的值依次递增。指定了某1项的值,则后面的依次递增,前面的从0开始依次递增。

enum weekday {

    MON,TUE,WEND,THUR=2,FRI,SAT,SUN

};

main(){

    enum weekday day = MON;

printf();

system("pause")

}

 

1.11 自定义类型

typedef int mytype ;

main(){

   mytype i = 123;

}

 

typedef void* jobject; // 任意类型的指针 对应java的object类型

typedef jobject jclass;

typedef jobject jstring;

typedef jobject jarray;

typedef jarray jobjectarray;

typedef jarray jbooleanarray;

typedef jarray jbytearray;

typedef jarray jchararray;

typedef jobject jweek;

typedef jobject jthrowable;

 

可以起到别名的作用,增加可读性。