C语言学习笔记

1. C语言基础知识

1.1 Linux下的第一个C程序

  • 创建hello.c文件
    vim hello.c
  • 编辑文件
#include <stdio.h>
int main()
{
   /* 我的第一个 C 程序 */
   printf("Hello, World! \n");
   return 0;
}
  • 编译代码
 gcc hello.c
  • 执行程序
./a.out
  • 输出 Hello,World!

1.2 C语言数据类型(64位编译器)

  • 整型
    short (2个字节)
    int(4个字节)/unsigned int (4个字节)
    long(8个字节)/unsigned long(8个字节)
  • 浮点型
    float (4个字节) 单精度
    double(8个字节) 双精度
  • 字符型
    char (1个字节)
  • 构造类型
    数组、结构体、共用体、枚举
  • 指针类型(*代表地址里面的值,&代表取地址)
    通过指针定义字符串:c语言定义字符串方法:char *p=“xikang”;(可以说p是字符串,但实际上p只是应该字符指针,本质上就是一个指针变量,只是p指向了一个字符串的起始地址而已。
  • 空类型(void)
  • 字符串常用操作:
    求长度:strlen();
    字符串连接:strcat();
    比较字符串:strcmp();
    字符串复制:strcpy();

1.3 标识符

  • 标识符是由字母、数字、下划线组成;
  • 第一个必须为字母或者下划线,且严格区分大小写
  • 标识符分为 关键字(if、else)、预定义标识符(define、scanf、printf、include)、用户标识符(xikang)。

1.4 关键字(重要)

static 的用法:
1) 用static 修饰局部变量:改变存储方式。变为静态存储方式,该变量在函数执行结束不会被释放,仍然保留在内存中。
2) 用static 修饰全局变量:改变其可见性。使其只在本文件内部有效。
3) 用static 修饰函数: 改变其连接方式。使函数只在本文件内部有效,不会与其他文件中的同名函数相冲突。
const 的用法:
1) 用const修饰常量: 定义时就初始化,以后不能更改。
2) 用const修饰形参: func(const int a) {}; 该形参在函数里不能改变。
3) 用const修饰类成员函数: 该函数对成员变量只能进行只读操作。

2. 输入输出

2.1 头文件

stdio.h 是一个头文件 (标准输入输出头文件) , #include 是一个预处理命令,用来引入头文件。 当编译器遇到 printf() 函数时,如果没有找到 stdio.h 头文件,会发生编译错误。

2.2 getchar() & putchar() 函数

int getchar(void) 函数从屏幕读取下一个可用的字符,并把它返回为一个整数。这个函数在同一个时间内只会读取一个单一的字符。可以在循环内使用这个方法,从屏幕上读取多个字符。

int putchar(int c) 函数把字符输出到屏幕上,并返回相同的字符。这个函数在同一个时间内只会输出一个单一的字符。可以在循环内使用这个方法,在屏幕上输出多个字符。

#include <stdio.h>
 
int main( )
{
   int c;
 
   printf( "Enter a value :");
   c = getchar( );
 
   printf( "\nYou entered: ");
   putchar( c );
   printf( "\n");
   return 0;
}

2.3 gets() & puts() 函数:

char *gets(char *s) 函数从 stdin 读取一行到 s 所指向的缓冲区,直到一个终止符。
int puts(const char *s) 函数把字符串 s 和一个尾随的换行符写入到 stdout。

#include <stdio.h>
 
int main( )
{
   char str[100];
 
   printf( "Enter a value :");
   gets( str );
 
   printf( "\nYou entered: ");
   puts( str );
   return 0;
}

2.4 printf() & scanf() 函数

2.4.1 printf

  • 使用前加头文件#include<stdio.h>
  • 格式说明:printf(“格式控制”,“输出内容”),第一部分把第二部分以指定形式展现出来。输出内容可以为空。
格式字符字符效果 (大写即输出英文为大写)
d有符号的的十进制
u无符号的的十进制
o无符号的八进制
x,X无符号的十六进制
f浮点数,小数形式输出
%.x f浮点数保留x位小数输出
e,E浮点数,指数形式输出
g,G浮点数,取e,f较短的输出,自动舍去末尾0
c单个字符
s输入字符串

示例1:

printf("%d, \n %d",12,21) 
输出:
12
21

示例2:

char s[6] = "abcde";
printf("%s\n",s);
printf("%s","abcde");
输出:
abcde
abcde

2.4.2 scanf

  • 格式说明: scanf(“格式控制”,地址表列);
  • 格式控制与printf基本一致,不一样的是scanf地址表列不能为空,对于每一个格式控制的输入都要有相应的变量对应,而且每一个输入变量前必须有一个&(取地址符

示例1:

int a,b,c;
scanf("%d%d%d",&a,&b,&c);
输出:
1,2,3

示例2:

char s1[255],s2[255];
scanf("%s%s",&s1,&s2);
printf("%s\n%s",s1,s2);
输入:abc def
输出
abc
def

示例3:(指定输入长度)

int x,y,z
scanf("%2d%4d%d", &x,&y,&z);
printf("%d,%d,%d", x,y,z);

输入:1234567
输出:12,3456,7
输入: 1空格234567
输出:1,2345,67

3.表达式、数据结构

3.1 表达式

3.1.1 赋值表达式

符号含义实例
<<=左移且赋值运算符C <<= 2 等同于 C = C << 2
&=按位与且赋值运算符C &= 2 等同于 C = C & 2
^=按位异或且赋值运算符C ^= 2 等同于 C = C ^ 2

3.1.2 算数表达式

加减乘除、取余、取模、自增、自减。

3.1.3 关系表达式

要么为真(1),要么为假(0)。
示例:

int x=1,y=0,z=2;
printf(x<y<z);
输出1;
先判断1<0,结果是0,再判断0<2,结果是1。

3.1.4 逻辑表达式

逻辑与: &&
位与: &
逻辑或: ||
位或: |
非 :!

3.2 数据结构

switch语句
有无break的区别,有break,匹配一个case,直接跳出,其他都不执行;无break,一个匹配,剩下全部执行。

4.函数

4.1 函数声明与调用

当您在一个源文件中定义函数且在另一个文件中调用函数时,函数声明是必需的。需要在调用函数的文件顶部声明函数
示例:

#include <stdio.h>
 
/* 函数声明 */
int max(int num1, int num2);
 
int main ()
{
   /* 局部变量定义 */
   int a = 100;
   int b = 200;
   int ret;
 
   /* 调用函数来获取最大值 */
   ret = max(a, b);
 
   printf( "Max value is : %d\n", ret );
 
   return 0;
}
 
/* 函数返回两个数中较大的那个数 */
int max(int num1, int num2) 
{
   /* 局部变量声明 */
   int result;
 
   if (num1 > num2)
      result = num1;
   else
      result = num2;
 
   return result; 
}

4.2 函数参数

  • 函数的默认返回值类型是int。
  • 函数参数可以是常量、变量、表达式、函数调用(递归、自己调用自己)
    调用类型 | 描述
    ----------------------------| -------
    传值调用 | 该方法把参数的实际值复制给函数的形式参数。在这种情况下,修改函数内的形式参数不会影响实际参数。
    引用调用 |通过指针传递方式,形参为指向实参地址的指针,当对形参的指向操作时,就相当于对实参本身进行的操作。

默认情况下,C 使用传值调用来传递参数。

4.3 常用库函数

  • abs() 取绝对值
  • sqrt() 开平方
  • fabs() 小数取绝对值
  • pow() 指数
  • sin() 正弦

5.指针

指针变量的本质用来存放地址,一般变量存放数值。

5.1 指针的定义(重要)

方式1:

int x=3; int *p=&x;

方式2:

int x=3;int *p;
p=&x;

*p是数值,p是地址,可以用在scanf函数中:

scanf("%d",p);
  • **p++ 和 (p)++的区别:前者地址会变,先进行地址+1,再取值。
    后者地址不会变,先去当前地址值,再将值+1。

示例:

#include<stdio.h>
int main(){
int *p,m[] ={1,3,5,7,9};
p=m;
int a=*p++;
int b=(*p++);
int c=((*p)++);
int d=(*p)++;
printf("%d,\n %d,\n %d,\n %d",a,b,c,d);
return 0;

}
输出:  1,
	   3,
	   5,
	   6

5.2 二级指针

*p : 一级指针,存放变量的地址。
**q : 二级指针,存放一级指针的地址。
示例:

int x=7;
int *p=&x, **q=p;
输出:
*p=7,*q=p,**q=7;

5.3 移动指针遍历字符串

char *s="xikang";
while(*s){ //
printf("%c",*s);
s++;
}

指向的内容不为空则进行循环,s代表地址,地址在变化。

5.4 指针在函数值传递与地址传递之间的应用(重要)

示例:交换两个数的值。
方法一:使用值传递,方法需要有返回值。
错误写法:

#include<stdio.h>
void fun(int a ,int b){
	int t;
	t=a;
	a=b;
	b=t;
}
main(){
	int x=1;
	int y=3;
	fun(x,y);
	printf("%d,%d",x,y);
}

原因在与函数fun()无返回值,在主函数调用时,仅仅对形参a,b进行了交换,并没有将交换后的值传递给实参x,y。
方法二:使用指针进行交换。


#include<stdio.h>
void fun(int *a ,int *b){
	int t;
	t=*a;
	*a=*b;
	*b=t;
}
main(){
	int x=1;
	int y=3;
	fun(&x,&y);//实参
	printf("%d,%d",x,y);
}

5.5 函数指针

函数指针本质上是一个指针,相对于常用的普通函数定义,其实就是把它函数名部分用指针来代替。常用作回调函数。

char (*fun)(char); //定义一个函数指针
char fun_1(char x) //定义一个函数体
{
    //函数内容
    return 0;
}
fun= & fun_1	//函数体与指针相关联

5.6 指针函数

一个返回指针的函数,其本质是一个函数。常用于返回数组、字符串等数据结构指针。
写法:

int *fun(int x,int y);

6.数组

6.1 一维数组初始化

int a[5]={1,2,3,4,5};合法
int a[5]={1,2,3,};合法
int a[ ]={1,2,3,4,5};合法
int a[5]={1,2,3,4,5,6};不合法
int  a[5];合法
int a[1+1];合法
int x=5,int a [x];不合法,数组个数必须是常量。
define P 5 int a[P]; 合法,P是符号常量。

6.2 一维数组的遍历

  • 求数组长度sizeof(arr)/sizeof(arr[0];strlen(char);
    以下代码用于求取数组所有元素的和、最大值、最小值以及平均值。
#include<stdio.h>
main(){
	int nums[] = {11,12,44,56,66};

int sum=0,avg=0,max=0,min=100;//默认数组中取值范围为0-100
int *p;//定义指针变量
int len =sizeof(nums)/sizeof(nums[0]);
for(p=&(nums[0]);p<=&(nums[len-1]);p++){
	sum+=*p;
	if(*p>max){
		max=*p;
		}
	if(*p<min){
		min=*p;
		}
}
printf("sum=%d avg=%f",sum,sum*1.0/len);
printf("max=%d min=%f",max,min);
}

6.3 二维数组初始化

int a[2][3]={1,2,3,4,5,6};合法
int a[2][3]={1,2,3,4,5, };合法
int a[2][3]={{1,2,3} {4,5,6}};合法
int a[2][3]={{1,2,3} {4,5, }};合法
int a[ ][3]={1,2,3,4,5,6};合法
int a[ ][3]={1,2,3,4,5,6};合法
int a[2][ ]={1,2,3,4,5,6};不合法,可以缺少行个的个数,不可以缺少列的个数。
  • a[2]用指针表示*(a+2);
  • a[2][3]用指针表示*(a+2)[3] 或者*(*(a+2)+3)

6.4 二维数组的遍历

四种方式遍历二维数组:

a[i][j]* (a[i]+j)* (*(a+i)+j)*(&a[0][0]+i*n+j)
#include <stdio.h>
void main(){
        int i,j;
        int a[3][3]={{1,2,3},{4,5,6},{7,8,9}};
 
        printf("方式一:a[i][j]\n");     //方式一
        for(i=0;i<3;i++){
                for(j=0;j<3;j++){
                        printf("%d",a[i][j]);
                }
        }
 
        printf("\n方式二:*(a[i]+j)\n"); //方式二
        for(i=0;i<3;i++){
                for(j=0;j<3;j++){
                        printf("%d",*(a[i]+j));
                }
        }
 
        printf("\n方式三:*(*(a+i)+j)\n"); //方式三
        for(i=0;i<3;i++){
                for(j=0;j<3;j++){
                        printf("%d",*(*(a+i)+j));
                }
        }
 
        printf("\n方式四:*(&a[0][0]+i*3+j)\n"); //方式四
        for(i=0;i<3;i++){
                for(j=0;j<3;j++){
                        printf("%d",*(&a[0][0]+i*3+j));
                }
        }
        printf("\n");
}

7 内存分配问题

内存分区示意图:
在这里插入图片描述

7.1 栈区

栈区介绍

  • 栈区由编译器自动分配释放,由操作系统自动管理,无须手动管理。
  • 栈区上的内容只在函数范围内存在,当函数运行结束,这些内容也会自动被销毁。
  • 栈区按内存地址由高到低方向生长,其最大大小由编译时确定,速度快,但自由性差,最大空间不大。
  • 栈区是先进后出原则。

存放内容

  • 临时创建的局部变量const定义的局部变量存放在栈区。

  • 函数调用和返回时,其入口参数返回值存放在栈区。

    7.2 堆

    堆区介绍

  • 堆区由程序员分配内存和释放。

  • 堆区按内存地址由低到高方向生长,其大小由系统内存/虚拟内存上限决定,速度较慢,但自由性大,可用空间大。

  • 分配内存使用malloc函数

void *malloc(size_t);

返回值是一个void*型的指针,该指针指向分配空间的首地址。

  • 释放内存使用free函数
void free(void * /*ptr*/);

参数是开辟的内存的首地址。

存放内容

  • 存放使用malloc创建的对象。

7.3 全局区/静态存储区

  • 通常是用于那些在编译期间就能确定存储大小的变量的存储区,但它用于的是在整个程序运行期间都可见的全局变量和静态变量。
  • 全局区有 .bss段 和 .data段组成,可读可写。

7.4 常量区

  • 字符串、数字等常量存放在常量区。
  • const修饰的全局变量存放在常量区。
  • 程序运行期间,常量区的内容不可以被修改。

7.5 代码区

  • 程序执行代码存放在代码区,其值不能修改(若修改则会出现错误)。
  • 字符串常量和define定义的常量也有可能存放在代码区。
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值