一、数组
1、一维数组
1》定义
2》初始化
3》使用
4》存储
2、多维数组
1》定义
2》初始化
3》使用
4》存储
3、变长数组
4、数组传参
地址传递:传数组名和元素个数
#include <stdio.h>
//void fun(int n,int b[])
void fun(int n,int *b)
{
int i;
for(i = 0; i < n; i++)
printf("%d\t",b[i]);
printf("\n");
}
int main(void)
{
int a[5] = {1,2,3,4,5};
fun(5,a);
return 0;
}
#include <stdio.h>
//void fun(int n,int m, b[][m])
void fun(int n,int m, int (*b)[m])
{
int i,j;
for(i = 0; i < n; i++){
for(j = 0; j < m; j++)
printf("%d\t",b[i][j]);
printf("\n");
}
}
int main(void)
{
int a[2][3] = {{1,2,3},{4,5,6}};
fun(2,3,a);
return 0;
}
二、指针
1、概念:
在C语言中,内存单元的地址称为指针,专门用来存放地址的变量,称为指针变量(pointervariable), 在不影响理解的情况中,有时对地址、指针和指针变量不区分,通称指针。
2、指针三要素:
当给一个指针初始化或者赋值之后,则该指针存在三要素,例如:
int a = 100;
int *pa = &a;
此时,对于pa来说,有以下三个表达式:
*p 表示p指向的数据
p 表示p的内容
&p 表示变量p在内存中的地址
3、指针的运算:
1》赋值: =
2》取值:*
3》取地址:&
4》加法运算:+
只能加一个整数
5》减法运算:-
可以减一个整数
也可以两个同类型的指针相减
6》关系运算:> < >= <= == !=
表示地址高低的比较
4、const关键字
5、多级指针
在C语言中,把存储指针变量地址的变量,称为多级指针变量,简称多级指针。
int a = 100;
int *p1 = &a; //p1为一级指针
int**p2 = &p1; //p2为二级指针
int ***p3 = &p2; //p3为三级指针
综上:n级指针变量中可以存放n-1级指针变量的地址。
#include <stdio.h>
int main(void)
{
int a = 100;
int *p1 = &a; //p1为一级指针
int **p2 = &p1; //p2为二级指针
int ***p3 = &p2; //p3为三级指针
printf("a = %d\n",a);
printf("*p1 = %d\n",*p1);
printf("**p2 = %d\n",**p2);
printf("***p3 = %d\n",***p3);
return 0;
}
6、void关键字
1》修饰函数返回值
表示该函数没有返回值
2》修饰函数的参数
表示该函数没有参数
3》定义指针
表示该指针中可以存放任何类型的地址,通常我把它称为通用指针。
#include <stdio.h>
int main(void)
{
void *p;
int a = 100;
p = &a;
printf("a : %d\n",a);
printf("*p : %d\n",*(int*)p);
char b = 'A';
p = &b;
printf("b : %c\n",b);
printf("*p : %c\n",*(char*)p);
float c = 3.4;
p = &c;
printf("c : %f\n",c);
printf("*p : %f\n",*(float*)p);
return 0;
}
三、指针和数组的关系:
1、指针可以指向一个数组,数组指针
2、数组中可以存储多个相同类型的地址,指针数组
#include <stdio.h>
int main(void)
{
#if 1
int a[5] = {1,2,3,4,5};
int *b[5] = {a,a+1,a+2,a+3,a+4};
int *p = a;
int * *pb = b;
int i;
for(i = 0; i < 5; i++)
printf("%d\t%d\n",p[i],*(p+i));
for(i = 0; i < 5; i++)
printf("%d\t",*pb[i]);
printf("\n");
#else
int a[2][3] = {{1,2,3},{4,5,6}};
int(*p)[3] = a;
int i,j;
for(i = 0; i < 2; i++)
for(j = 0; j < 3; j++)
printf("%d\t%d\t%d\n",p[i][j],*(p[i]+j),*(*(p+i)+j));
#endif
return 0;
}
3、带参的主函数
#include <stdio.h>
/**
argc : 命令行参数个数
argv : 命令行参数
*/
//int main(int argc, char *argv[])
int main(int argc, char **argv)
{
int i;
printf("argc = %d\n",argc);
for(i = 0; i < argc; i++)
printf("%s\n",argv[i]);
return 0;
}
四、函数
1、基本概念
1》函数的定义
2》函数的声明
3》函数的调用
4》实参,形参及它们的关系
2、函数传参
1》传参方式:
1)值传递:
在被调用函数中不能改变实参
实参类型和形参类型相同
2)地址传递
在被调用函数中可以改变实参
形参类型是实参类型对应的指针类型
2》根据实参的类型可以将传参分为以下几种
1)实参是常量
值传递
2)实参是基本类型变量
值传递
地址传递
3)实参是数组
地址传递:传数组名和元素个数
4)实参是指针变量
值传递:#include <stdio.h>
void fun1(int *p1,int *p2)
{
int *t,b;
//不能改变实参的值
t = p1;
p1 = p2;
p2 = t;
//可以改变实参指向的数据
b = *p1;
*p1 = *p2;
*p2 = b;
}
void fun2(int **p1,int **p2)
{
#if 0
int *t;
//可以改变实参的值
t = *p1;
*p1 = *p2;
*p2 = t;
#else
//实参指向的数据也可义改变
int b;
b = **p1;
**p1 = **p2;
**p2 = b;
#endif
}
int main(int argc, char **argv)
{
int a = 3,b = 5;
int *pa = &a, *pb = &b;
#if 0
printf("pa = %p,pb = %p\n",pa,pb);
printf("a = %d\t,b = %d\n",a,b);
fun1(pa,pb); //值传递,在被调用函数中不能改变pa和pb
printf("pa = %p,pb = %p\n",pa,pb);
printf("a = %d\t,b = %d\n",a,b);
#endif
#if 1
printf("pa = %p,pb = %p\n",pa,pb);
printf("a = %d\t,b = %d\n",a,b);
fun2(&pa,&pb);//地址传递,在被调用函数中可以改变pa和pb
printf("pa = %p,pb = %p\n",pa,pb);
printf("a = %d\t,b = %d\n",a,b);
#endif
return 0;
}
3) 返回函数的地址
4)返回值字符型指针
#include <stdio.h>
int fun1(void)
{
int x = 123;
return x;
}
char * fun2(void)
{
return "hello world";
}
int *fun3(int n,int a[])
{
int i;
for(i = 0; i < n; i++)
a[i] *= 2;
return a;
}
void fun(void)
{
printf("hello world\n");
}
typedef void(*TYPE)(void);
//void(*fun4(void))(void)
TYPE fun4(void)
{
return fun;
}
char *fun5(void)
{
//char p[] = "hello world"; //p不能返回
char *p = "hello world"; //p可以返回
return p;
}
int main(int argc, char **argv)
{
#if 1
char *str;
str = fun5();
printf("%s\n",str);
#else
void (*p)(void);
p = fun4();
p();
int arr[5] = {1,2,3,4,5};
int *p,i;
for(i = 0; i < 5; i++)
printf("%d\t",arr[i]);
printf("\n");
p = fun3(5,arr);
for(i = 0; i < 5; i++)
printf("%d\t",arr[i]);
printf("\n");
#endif
return 0;
}
通用行Makefile,在当前目录下需要生成多个可执行文件是直接make,同时根据时间戳分别一一生成对应文件名的可执行文件
CC = gcc
CFLAGS = -Wall -g -O0
#wildcard 执行make时,将当前目录下所有*.c的文件名赋给SRC
SRC = ${wildcard *.c}
#patsubst 根据第一个参数,在第三个参数中匹配对应的文件名,匹配成功则将其转换成第二个参数的格式
OBJS = ${patsubst %.c,%,$(SRC)}
#all为伪目标
all:$(OBJS)
%:%.c
$(CC) -o $@ $^
clean:
$(RM) $(OBJS)