C语言基础

C语言基础

这是很简单的C语言,如果想深入了解,可以自己自学,C语言毕竟可以说是高级编程语言的基础呀,书籍一堆一堆的。
我们要学习C语言,使用C语言开发要怎么搞呢?
首先就是一个必要的开发工具,这里推荐开源工具Dev-C++,一款用于编译C语言的工具,就像eclipse开发java一样,除了安装Dev-C++之外,不在需要其他工具了。

C之Hello world

打开Dev-C++工具,直接选择新建或者快捷键CTRL+N新建一个C文件,然后我们输入以下代码:

#include    
#include   
main(){    
       printf("helloworld!\n");  //System.out.println();   "\n"换行符
       system("javac Hello.java");
       system("java Hello");
       system("notepad");
       system("pause"); //system执行windows的系统命令
       }

然后保存到命名为C 的目录中,将文件保持为hello.c,但这只是一个原始代码,继续在Dev-C++中的菜单栏找到运行,然后选择编译,这时你会发现在hello.c的旁边出现一个hello.exe。
哈,是不是很神奇,我们自己搞出一个exe可执行文件!
当然我们现在还不用执行hello.exe,我们要在C目录下添加一个Hello.java(标准的java文件)的文件夹,里面输入Hello,java就行了。
这时执行hello.exe,就会打开命令提示符,也就是CMD命令框。
首先会输入“helloworld!”,然后编译,接着执行Hello.class,最后停止。

这便是运行流程。那么我们来说说文件中代码的含义吧!
其中

#include    // 相当于 java的import .h c的头文件 stdio.h standard io 标准输入输出
#include   // stdlib standard library 标准函数库    java.lang

在写C语言的时候添加上这两句话准没错的。
然后main(){ }这是一个主程序入口,就类似于java中的public static void main(String[] args)。
最后就是system(),这相当于system执行windows的系统命令 ,所以在执行system("javac Hello.java");是相当于我们在命令提示符中直接输入javac Hello.java。

C语言的基本数据类型

前面我们已经学会了C语言的Hello world,现在我们要了解一下C语言的数据类型了,我们直接通过与java对比,看看他们直接有多大区别

java基本数据类型  所占字节            C数据类型   所占字节
boolean             1   
byte            1
char            2               char        1个字节
short           2               short       2
int             4               int         4
long            8               long        4
float           4               float       4
double          8               double      8

上面是java和C直接都有的数据类型,同时对比双方所占字节数,但是上面并不是C所有的数据类型,C一共有以下几种数据类型:

char, int, float, double, long, short, signed, unsigned, void
* signed 有符号数 最高位是符号位 可以表示负数 但是表示的最大值相对要小
* unsigned 无符号数 最高位是数值位 不可以表示负数  表示的最大值相对要大
* signed unsigned 只能用来修饰整形变量 char short int long
* C没有 boolean  byte C用0和非0表示false true

上面我们了解C所占的字节数,但是如何验证呢?可以通过以下代码验证:

#include    
#include
// char, int, float, double, long, short, signed, unsigned, void
// signed unsigned 有符号 无符号  只能用来修饰整形变量  char int short long 默认有符号
// sizeof(int)

main(){    
   printf("char占%d个字节\n", sizeof(char));
   printf("int占%d个字节\n", sizeof(int));
   printf("short占%d个字节\n", sizeof(short));
   printf("float占%d个字节\n", sizeof(float));
   printf("long占%d个字节\n", sizeof(long));
   printf("double占%d个字节\n", sizeof(double));
   unsigned char c = 128;
   printf("c = %d\n",c);

       system("pause");
       }

其中%d表示的是int类型的占位符,请大家注意,在使用占位符的时候千万不要写错!然后sizeof(int)这个方法表示的是其中的变量所占的字节数。

占位符的使用

刚才说到%d这个占位符,那么其他的占位符又是怎样的呢?

%d  -  int
%ld – long int
%lld - long long
%hd – 短整型
%c  - char
%f -  float
%lf – double
%u – 无符号数
%x – 十六进制输出 int 或者long int 或者short int
%o -  八进制输出
%s – 字符串

以上就是C语言中的少数占位符,使用代码如下:

#include    
#include    

main(){    
      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);
      printf("s = %hd\n", s);
      printf("i = %d\n",i);
      printf("l = %ld\n",l);
      printf("f = %.4f\n",f);  //默认输出的是6位有效数字的小数 想手动指定 加上.X
      printf("d = %.7lf\n",d);
      printf("%#x\n",i);
      printf("% #o\n",i);
      //char cArray[]={'a','b','c','d','\0'};  // C数组[]要写在变量名后面, \0表示的是字符串结束符,这种方式不能输入汉字
      //char cArray[]="abcdefg";    // 这种方式不用手动写入结束符
      char cArray[]="你好";   // 输入汉字
      printf("cArray = %s",cArray);
       system("pause");
       }

C语言之输入函数

学习计算机语言逃不过一个宿命,那就是讨论内存地址,而C语言中更是讲究。
在这里我们先通过键盘输入班级人数,然后通过取出count的地址来保存班级人数

#include    
#include
//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");
       }

C中的指针

C语言中的指针可以说是一个重点难点,比较难以理解,因为我们内存地址让人难以理解,所以这里只是抛砖引玉的粗略的讲讲指针的概念。

关于符号*

  其中int* pointer 表示声明一个int类型的指针变量pointer
  x * y  表示乘法运算      
  *pointer; 取出指针变量pointer 中保存的内存地址对应的内存中的值

接着我们来看看指针的运用:

#include    
#include    

main(){  
       int i = 123;
       //一般计算机中用16进制数来表示一个内存地址
       printf("%#x\n",&i);
       //int* int类型的指针变量  pointer指针  指针变量只能用来保存内存地址
       //用取地址符&i 把变量i的地址取出来 用指针变量pointer 保存了起来
       //此时我们可以说 指针pointer指向了 i的地址
       int* pointer = &i;
       //int *pointer ; int * pointer   
       printf("pointer的值 = %#x\n",pointer);
       printf("*pointer的值%d\n",*pointer);
       *pointer = 456;
       printf("i的值是%d\n",i);
       system("pause");
       }

运行这段代码,会得到以下结果:

0x62fe44
pointer的值 = 0x62fe44
*pointer的值123
i的值是456  

其中i的值为123,但是计算机给i分配一块内存地址既是0x62fe44这个空间,然后这个空间存放的值为123,这便是i=123的含义。
然后我们在创建了int类型的指针变量point,当然指针变量point也获得了自己的一个内存地址。
同时通过&i将i的内存地址取出,赋予了point,也就是说指针变量point的内存地址的值存入了0x62fe44这个内存地址,而通过 0x62fe44内存地址是可以找到123这个值的。
那么如何在指针变量point中取出123呢?使用pointer便可以取出。
最后
pointer = 456就是将0x62fe44这个内存地址的值重新赋值为456,而i的内存地址是指向0x62fe44的,所以当0x62fe44的值发生改变时,i的值也随之发生改变。
这里面有点绕,不好理解,但指针方面的知识确实要好好学习一下。

指针的错误使用

在使用指针之前要注意对其进行赋值,没有赋值就进行操作的指针被称为野指针,在Windows中是不允许这样操作的,因此指针使用之前要初始化 赋给它一个自己程序中声明的变量的地址
其次,指针使用的时候要注意 int类型的指针要指向int类型的内存地址, double类型的指针要指向double类型的地址,如果乱指会出bug
下面是错误的示例代码:

#include    
#include    
/**
*/
main(){  
         //注意,这是错误的示例代码
         int i;
         double d = 3.1415;
       int* pointer = &d;
       printf("pointer的值=%#x\n",pointer);
       printf("*pointer = %d\n",*pointer);
       system("pause");
       }

C语言交换两数的值

一个传统的问题,如何交换两数的值,以下代码演示

#include    
#include    
/**
*值传递 和引用传递  值传递和引用传递传递的实际上 都是数值  只不过引用传递传递的是地址值
*如果想通过一个子函数来修改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); // 无法交换两数的值
       swap2(&i,&j); // 通过交换内存地址中的值,成功交换两数的值
       printf("i的值%d,j的值%d\n",i,j);
       system("pause");
       }

为什么swap方法无法交换i和j的值呢?举个不恰当的例子,那边是局部变量和成员变量的区别,首先swap外部有i和j,然后传入swap之后,i和j便与外部的i和j断绝了关系,因此在内部交换之后无法将交换信息传递到外部。
而swap2采用的是引用传递,通过获取内存地址,改变内存地址之中的值,这时才能真正的改变两数的值

c中数组与指针的关系

数组实际上就是一块连续的内存空间,而数组变量名的地址实际上是第一个元素的地址,因此我们通过指针获取数组变量名的内存地址,可以说是获得了数组的内存地址。
同时我们通过对内存地址的加减法就能获得数组中的元素。需要注意的是对内存地址的加减法中,相加得到的结果实质上与数据类型有关,如我们使用指针变量*(pointer+0),如果是int类型的数组,实质上会加上4个字节,得到下一个数组元素,char类型的数组也是一样

#include    
#include    

main(){             
  // char array[] = {'a','b','c','d','\0'};
   int array[] = {1,2,3,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;
    //int* pointer = &array;
    char array2[] =  "hello from c"
   char* pointer2="hello from c";
   //printf("%s\n",pointer2);
   /*
   // char类型中每次运算是相加1个字节
   printf("*(pointer+0)=%c\n",*(pointer+0));
   printf("*(pointer+0)=%c\n",*(pointer+1));
   printf("*(pointer+0)=%c\n",*(pointer+2));
   printf("*(pointer+0)=%c\n",*(pointer+3));
   */
   // 在int类型数组中,指针变量每次运算结果是相加4个字节
   printf("*(pointer+0)=%d\n",*(pointer+0));
   printf("*(pointer+1)=%d\n",*(pointer+1));
   printf("*(pointer+2)=%d\n",*(pointer+2));
   printf("*(pointer+3)=%d\n",*(pointer+3));

       system("pause");
       }   

C语言中指针变量的字节数

指针变量的所占字节数与所指向的数据类型无关,与操作系统有关,32位操作系统地址总线是32位,用4个字节的变量来保存32位操作系统的内存地址 1byte 8位 4*8=32
因此32位操作系统 指针变量占4个字节,而64位操作系统 指针变量占8个字节 。

#include    
#include    
/**
32位操作系统地址总线是32位   4个字节的变量来保存32位操作系统的内存地址 1byte 8位  4*8=32
32位操作系统 指针变量占4个字节
64位操作系统 指针变量占8个字节
*/
main(){    
      int* pointer;
      double* pointerD;
      printf("int类型的指针变量占%d个字节\n",sizeof(pointer));
      printf("double类型的指针变量占%d个字节\n",sizeof(pointerD));
       system("pause");
       }    

C语言之多级指针

多级指针:数星星,有几个星就是几级指针。其中二级指针只能保存一级指针的内存地址,同意三级指针只能保存二级指针的内存地址。
那么如果想通过三级指针拿到最初的变量的值,就使用 ***point3

#include    
#include    

main(){    
      int i = 123;
      //int类型的一级指针
      int* pointer = &i;
      //int类型的二级指针 二级指针只能保存一级指针的地址
      int** pointer2 = &pointer;
      //int类型的三级指针
      int*** pointer3 = &pointer2;
      // 取出初始值
      printf("***pointer3 = %d\n",***pointer3);
       system("pause");
       }

C主函数获取临时变量的地址

main函数获取子函数中临时变量的地址,此时要注意值传递和引用传递的区别,我们想要获得的是内存地址,因此必须传入内存地址,同时二级指针才能保存一级指针的内存地址,因此子函数中要传入的变量应为二级指针

#include    
#include    

function(int** pointer){
   int i = 123;
    *pointer = &i;  
    printf("i的地址%#x\n",&i);    
          }

main(){    
      int* pointer1;
      function(&pointer1);
      printf("pointer1的值%#x\n",pointer1);
       system("pause");
       }

C语言中的内存地址回收

栈内存由系统统一分配统一回收
静态内存分配 栈内存大小固定的 内存地址是连续的

#include    
#include    

int* getData(){
     int array[] ={1,2,3,4,5};
     printf("%#x\n",&array);
     return &array;
     }
int* getData2(){
     int array[] ={5,4,3,2,1};
     printf("%#x\n",&array);
     return &array;
     }

main(){    
     int* pointer = getData();
     getData2();
     printf("%d,%d,%d\n",*(pointer+0),*(pointer+1),*(pointer+2));
     printf("%d,%d,%d\n",*(pointer+0),*(pointer+1),*(pointer+2));
     printf("%#x\n",pointer);
       system("pause");
       }

在这一例子中,pointer指向的是getData()中的内存地址,可是为什么得到的结果却是getData2()的值呢?那是因为在执行完getData()之后,获得的栈内存地址被回收,然后直接被getData2()使用,导致其中的值被修改。所以使用指针的时候要注意,栈内存只能使用一次,之后会出现同一块内存地址被其他程序占用的情况。如果想让内存始终保持一个只,那么则要使用堆内存

c中的动态内存分配

java new对象就会申请一块堆内存,而C语言则要手动申请,使用malloc申请一个固定的内存大小,如malloc(sizeof(int)*5)申请5个int类型大小的内存,注意申请了堆内存需要手动释放,如果忘记释放容易造成内存泄漏。
其中对内存的特点:不连续的;大小不固定,取决机器的状态

#include    
#include    
//c   malloc memory allocation 内存分配
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");
       }     

学生学号管理系统

#include    
#include    
/**
保存班级人数
申请一块堆内存保存学生的学号
来了几个插班生
扩展一下堆内存
保存插班生的学号
realloc re-
*/
main(){
       printf("请输入班级的人数:");
       int count;
       scanf("%d",&count);
       //申请一块堆内存
       int* pointer = malloc(sizeof(int)*count);
       int i;
       for(i = 0;i

C的结构体

c结构体类似java的class,使用struct来声明c的结构体。
结构体的大小大于等于结构体中每一变量的占字节数的和,
结构体的大小是最大的那个变量所占字节数的整数倍。如果想获得结构体中的变量,可通过获取结构体指针,然后通过结构体指针获得结构体变量
注意C结构体中不能定义函数,如果想要在结构体中调用方法,可以使用函数指针。
函数指针的定义的方式:返回值(函数指针变量名字)(返回值),如下面示例代码中的void(studypointer)();
其中 -> 表示间接引用运算符,可以访问结构体里面的变量,如示例代码中的stuPointer->age

结构体的特点
  • 结构体中的属性长度会被自动补齐,这是为了方便指针位移运算
  • 结构体中不能定义函数,可以定义函数指针
  • 程序运行时,函数也是保存在内存中的,也有一个地址
  • 结构体中只能定义变量
  • 函数指针其实也是变量,它是指针变量
  • 函数指针的赋值: 函数指针只能指向跟它返回值和接收的参数相同的函数
 #include    
 #include    

 void study(){
            printf("good good study!\n");
            }
 struct Student{
       int age;  //8
       int score;  // 4
       char sex;   //1
       void(*studypointer)();
       } ;
 main(){    
  struct Student 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);
  //printf("stu.age = %hd\n",stu.age);
  //printf("stu.score = %d\n",stu.score);
  //printf("stu.sex = %c\n",stu.sex);
 // printf("结构体student占%d个字节\n",sizeof(stu));
        system("pause");
        }

C中的联合体

联合体占字节数取决于,其中成员占内存空间最大的那一个 。
联合体只能保存一个变量的值,并且联合体共用同一块内存。

#include    
#include    
/**

*/
union u{
      int num; //4个字节
      double d; //8个字节
      }
main(){    
   union u u1;
   u1.num = 123;
   u1.d = 123.456;
   printf("union占%d个字节\n",sizeof(u1));
   printf("u1.num = %d\n",u1.num);
       system("pause");
       }

C中的枚举

C中的枚举使用enum,同时枚举中的元素可赋值

#include    
#include    
/**
*/
enum weekday{
     MON=9, TUE, WEND,THUR,FRI,SAT,SUN
     };
main(){    
   enum weekday day = MON;
   printf("day= %d",day);
       system("pause");
       }    

C中的自定义类型

自定义类型使用typedef可给类型赋予别名,如 typedef int i;此时能将i当作int类型使用。
用途:如果结构体名字过长,可起到省略的作用,其中在Android的jni中大量使用了typedef设置别名,便于辨认数据类型

// 简单实例:
#include    
#include    
/**
*/
typedef int i;
main(){    
           i j = 123;
           printf("j = %d\n",j);
       system("pause");
       }

如在之前的结构体中有

       struct Student{
             ···
             } ;

可更改为:

typedef struct Student{
int age; //8个字节
int score; // 4个字节
char sex; //1个字节
// 使用函数指针调用函数
void(*studypointer)();
} stud;

此时在main函数中就可以直接使用stud代替Student,如下:

main(){    
 stud stu = {18,100,'f'};
 // 函数指针指向函数的内存地址
 stu.studypointer = &study;
 ····

}

你可能感兴趣的:(C语言基础)