零巫_的c语言笔记

小技巧:

  • 在iso646.h中可以用and代替&&,or代替||,not代替!。
  • c语言之所以不适用前者,是因为c语言总是尽量少的保留关键字。
  • 判断是否为空白字符的几种方法:
  • 代码一
c != ' ' && c != '\n' && c != '\t' /* 如果c不是空白字符,该表达式为真*/
  • 或者可以这样
c == ' ' || c == '\n' || c == '\t' /*如果c是空白字符,该表达式为真*/
  • 最好利用ctype.h中提供的函数,利用isspace()函数的返回值,空白字符为真,非空白字符为假。
  • 注意’/'运算符的特殊性
int a = 1;
float pi;
pi = 1 - a / 3;//此处的pi值为0,因为整形除以整形为整形,会溢出数字。需要至少一个数为浮点数。
printf("%f",pi);
  • 对数字进行移位运算时,必须说明其类型,否则右移时左边会自动补零。
  • 计算26个字母的个数时,注意首尾
a = a + k - 1;
		if (a > 'Z')
		{
			a = a - 'Z' + 'A' - 1;//此处的-1及其容易漏掉,否则就头尾都没有加上
			return a;
		}
  • 循环移位的技巧(位运算)
x = (unsigned int)x>>n | x << 32-n//移动n位
  • float类型为小数点位后六位,可能会出现莫名奇妙的溢出。所以当需要使用正好是小数点位后六位时,建议选择双精度浮点型。

  • c语言中对于下面的语句认为真(输出为ok),比较两个数时对其类型要求并不十分严格!

#include
int main()
{
	float m = 2.0, n = 1.0;
	int a = 2;
	if (m / n == a)
		printf("ok!");
	return 0;
}
  • 将数据限制在1~max之间的语句
x = x % max + 1;
  • 注意:字符映射函数并不会改变参数的值,只是会返回已修改的值。
tolower(ch); // 不影响ch的值
ch = tolower(ch); // 把ch转换成小写字母
  • 头文件ctype.h中提供了很多字符测试函数和字符映射函数,用来鉴定输入字符的特定类型。(详见c primer plus中7.2.2节)
  • c语言中尽量多用乘法而少用平方根函数,两者的算力是有差别的!
  • 读取数值时对负数的处理
while(isspace(s[i])) i++;//跳过前导空白符
sign=(s[i]=='-') ? -1: 1;//判断符号
  • 将接收的数字字符转化成对应的数
double val;
for(val=0;isdigit(s[i]);i++)
{
	val=10*val + (s[i-'0']);
}
  • 处理小数部分(与上面类似)
for(i=1,power=0.1;isdigit(s[i]);i++,power*=0.1)
{.
	val+=(s[i]-'0')*power;
}
  • 两个数比较的三种情况
a<b?-1: a>b?1:0//a小返回-1,大返回1,相等返回0

将一群人看作一圈人的方法(首尾相连)

  • 约瑟夫问题
# include
int main(void)
{
    int M,N;
    scanf("%d %d",&M,&N);
	int a[M], b[M];
   int i, j, k;
   for (i=0; i<M;i++)
   	a[i]=i+1;
   for (i=M,j=0; i>1; i--)
   {
   		for (k=1; k<=N; k++)
   			if(++j>i-1) j=0;/*最后一个人报完数后,
   			第一个人接着报数,形成一个圈*/
    	b[M-i]=j? j-1: i;//与上面圈对应的操作
    	if(j)//与上面圈对应的操作!
        for(k=--j; k<i; k++)
            a[k] = a[k+1];
   }
   //for(i=0; j
    //printf("%6d", b[i]);
   printf("%d", a[0]);
   return 0;
}

字符串的声明

#include 
#include 

int main()
{
    //定义字符串的几种方式
    //字符串和字符数组的区别:最后一位是否是空字符
    char names1[] = {'j', 'a', 'c', 'k', '\0'};
    char names2[50] = "jack";
    char * names3 = "jack";

    printf("数组所占空间大小:%d\n", sizeof(names1));
    printf("数组所占空间大小:%d\n", sizeof(names2));

    //动态输入
    printf("请输入新名称: ");
    scanf("%s", names2);
    printf("%s\n", names2);

    return 0;
}
/*
注意:
声明存储字符串的数组时,数组大小至少比所存储的字符串多1,因为编译器会自动在
字符串常量的末尾添加空字符\0
*/


布尔类型的应用实例

// divisors.c -- 使用嵌套if语句显示一个数的约数
#include 
#include 
int main(void)
{
     unsigned long num;          // 待测试的数
     unsigned long div;          // 可能的约数
     bool isPrime;               // 素数标记

     printf("Please enter an integer for analysis; ");
     printf("Enter q to quit.\n");
     while (scanf("%lu", &num) == 1)
     {
          for (div = 2, isPrime = true; (div * div) <= num; div++)
          {//上面的布尔类型isPrime可利用逗号表达式进行再次定义为真
               if (num % div == 0)
               {
                    if ((div * div) != num)
                    printf("%lu is divisible by %lu and %lu.\n",
                              num, div, num / div);
                    else
                         printf("%lu is divisible by %lu.\n",
                              num, div);
                    isPrime = false;    // 该数不是素数
               }
          }
          if (isPrime)//若程序未进入上面的if语句,就执行此if语句!
               printf("%lu is prime.\n", num);
          printf("Please enter another integer for analysis; ");
          printf("Enter q to quit.\n");
     }
     printf("Bye.\n");

     return 0;
}
  • 由上面的例子可以看出,布尔类型的设立就是为了标记!将一些难以用表达式写出的结果用_Bool类型的0或者1进行表示。

循环与选择语句用法

  • 注意循环条件:区别字符串尾’\0’和换行符’\n’的区别(本人非常容易写错!!!)
  • do-while语句基础语法:
do
{
	语句;
} while(条件);
  • switch-case语句语法
#include 
int main(void){
    int a;
    printf("input integer number:      ");
    scanf("%d",&a);
    switch (a){
        case 1:printf("Monday\n");
        case 2:printf("Tuesday\n");
        case 3:printf("Wednesday\n");
        case 4:printf("Thursday\n");
        case 5:printf("Friday\n");
        case 6:printf("Saturday\n");
        case 7:printf("Sunday\n");
        default:printf("error\n");
    }
    return 0;
}
  • scanf()函数读取数字时不读取’\n’,所以在读取数字后应该用getchar();刷新缓冲区。
  • 循环接收读入与停止技巧)(学习此技巧)
printf("Enter an integer (q to quit):\n");
while(scanf("%lu", &number) == 1)//此处技巧性极强
{
	printf("Binary equivalent:");
	to_binary(number);
	putchar('\n');
	printf("Enter an integer (q to quit):\n");
}
  • 利用计时器的特性来判断循环是否完全执行
#include
#include

int isprime(int n);

int main()
{
	long m;
	int i = 0;
	scanf_s("%ld", &m);
	while ((int)pow(2, i) - 1 <= m)
	{
		if (isprime(i) && isprime(pow(2, i) - 1))
			printf("M(%d)=%d\n", i, (int)pow(2, i) - 1);
		i++;
	}
	return 0;
}

int isprime(int n)//注意此函数是如何判断循环体是否完全执行完毕的!
{
	int i;
	for (i = 2; i < n; i++)
	{
		if (n % i == 0)
			break;
	}
	if (i == n)
		return 1;
	else
		return 0;
}

多出口循环导致累加错误

  • 多出口时,要注意把累加器累加写在循环外面,否则会跳出循环,导致累加器没有累加成功,从而形成死循环。
/***在下面Begin至End间,按原型 void diceGame(int randSeed) 定义函数***/
/********** Begin **********/
#include
void diceGame(int randSeed);

int main()
{
    diceGame(10);
    return 0;
}

void diceGame(int randSeed)
{
    srand(randSeed);
    int count = 1;
    do
    {
        if (count > 1)
        {
            if (x == 7)
            {
                printf("Round %d:  Score:7  Failed!\n", count);
                break;
            }
            else if (x == last)
            {
                printf("Round %d:  Score:%d  Success!\n", count, x);
                break;
            }
            else
            {
                printf("Round %d:  Score:%d  Cooutinue!\n", count, x);
                continue;
            }
        }
        //count++;若把累加器写在此处可能会出现问题(多出口循环!)
    } while (1);
    count++;
}
/********** End **********/

与数组的搭配使用示例

  • 打印杨辉三角
#include 

int main(void)
{
    int length;
    scanf_s("%d", &length);
    int nums[100][100];
    int i, j;
    /*计算杨辉三角*/
    for (i = 0; i < length; i++)
    {
        nums[i][0] = 1;
        nums[i][i] = 1;
        for (j = 1; j < i; j++)
            nums[i][j] = nums[i - 1][j - 1] + nums[i - 1][j];
    }

    /*打印输出*/
    for (i = 0; i < length; i++)
    {
        for (j = 0; j < length - i - 1; j++)
            printf("  ");
        for (j = 0; j <= i; j++)
            printf("%-4d", nums[i][j]);
        putchar('\n');
    } 
    return 0;
}

编译预处理

  • 编译预处理用来交换两个数(注意语法——小括号不能掉!)
#include 
#define swap(x,y) {(x) = (x) ^ (y); (y) = (x) ^ (y); (x) = (y) ^ (x);}

int main()
{
    int x, y,z;
	scanf("%d%d%d", &x,&y,&z);
	if(x>y) swap(x,y); 
	if(x>z) swap(x,z); 
	if(y>z) swap(y,z); 
	printf("%d %d %d", x,y,z);
    return 0;
}

数据类型的取值范围

数据类型 字节数 取值范围
signed char 1 -128 ~ 127
unsigned char 1 0 ~ 256
short 2 -32768 ~ 32767
int 4 -2147483648 ~ 2147483647
long 4 -2147483648 ~ 2147483647
long long 8 19位数
float 4 1.17549 * 10(-38) ~ 3.40282 * 10(38)

位运算的运用示例

  • 运用位运算进行交换两个数(一个整数与另外两个整数异或两次之后还是它本身!)
  • 注意当两个数相等时,不能进行异或。(相当于一个数与自己异或三遍,结果为零)
if(m == n) return;
m=m^n;
n=m^n;
m=n^m;
/*用位运算实现交换m和n,使用异或*/

使程序容错率更高的注意事项

  • 除法注意分母为零的情况,使用if语句进行判断。
  • 对于有些可能出现小数的数据应该使用浮点型。

函数

c语言复习备考

实参的求值顺序

实参的求值顺序是未定义行为,由编译器决定。

所以一般避免使用会引起副作用的实参表达式

作用域和可见性

全局变量的的作用域属于文件范围

extern是对全局变量的引用性声明(而非定义)

存储类型

存储类型一共有四种类型:auto , extern , static , register

  • auto:自动变量(默认类型)
  • extern:全局变量的存储类型(但是不能在定义时写出)
  • static:定义静态变量
    • 静态局部变量:与全局变量一样,拥有永久的生命周期。
    • 静态全局变量:限定在此文件中访问

getchar() 和putchar()

  • 它们一般是预处理的宏,而不是真正的函数。
  • 只读取字符,所以不用进行转换说明。
  • 技巧:可以习惯把读取和测试表示为一个表达式,更加简约。
ch = getchar();         /* 读取一个字符 */
while (ch != '\n')      /* 当一行未结束时 */
{
     ...                /* 处理字符 */
     ch = getchar();    /* 获取下一个字符 */
}

例如这种替换(经典 )

while ((ch = getchar()) != '\n')
{
     ...                /* 处理字符 */
}
  • 这样处理尽管会降低程序的可读性,但是是一般程序员的写法。应该学会这种思想。
  • 常用getchar()来读取字符最后的一个反斜杠,避免下一个scanf语句读取到反斜杠而进行自动换行。
  • 关于scanf()函数接受的字符与整型问题:
  • 注意:输入的回车字符会自动被送出缓冲区!
#include
int mian()
{
	char ch;
	printf("请输入一个字符:(输入ctrl + z结束)");//输入A和'\n'会先处理A后,再直接换行
	while ((ch = getchar()) != EOF)
	{
		if (isupper(ch))
		{
			ch = ch + 32;
		}
		putchar(ch);
	}
	return 0;
}

getchar()与for语句联合使用

#include
int main()
{
	unsigned int count=0;
	char num;
	int sum=0;

	printf("请输入一个十进制正整数:");
	for (;num = getchar(), num != '\n'; count++)//注意空语句的使用!
	{
		sum += num - 48;
	}
	printf("是%d位数,其各位数之喝是%d!", count, sum);
	return 0;
}
char a;//用户输入数字3
scanf("%d", &a);
printf("%d", a); //输出值为51,为字符'3'的ASCII码!

所以scanf()函数接收数据时与定义变量时的类型一致!

fgets()函数

  • fgets(char* a, int n,char* c)
  • 从c中读取不超过n的一行字符串
  • fgets()函数会自动在字符串末尾加上一个空字符

pow()函数

  • 原型:pow(int a, int b)
  • pow()函数返回的值为整形!

qsort()函数

  • 功能:实现自主选择对各种数据类型的排序
  • 原型:void qsort (void *base,int nelem, int width, int ( * fcmp)(const void * , const void * )
  • 头文件:stdlib.h
#include 
#include 
#include 
int s[10000],n,i;
int cmp(const void *a,const void *b)
{
return(*(int *)b-*(int *)a);  //实现的是降序排序
}
int main()
{

// 输入想要输入的数的个数
scanf("%d",&n);
for(i=0;i<n;i++)
scanf("%d",&s[i]);
qsort(s,n,sizeof(s[0]),cmp);
for(i=0;i<n;i++)
printf("%d ",s[i]);
return(0);
}

常用简写方式带来的问题

  • 例如,数学上我们习惯写s = t(t-a)(t-b)(t-c),但是编程时不能这样写,编译器会报错。
s = t(t-a)(t-b)(t-c)//编译器会报错:t()不是一个函数

ctype.h系列函数

  • 见《C primer plus》P156

常用字符串处理函数

  • 实践案例
#define _CRT_SECURE_NO_WARNINGS
//成绩排序
#include
#include
void sorted(char* pname[], int* grade, int number);
int button;
int number;
char name[100][100];
char* pname[100];
int grade[100];
int main()
{
    while (scanf("%d", &button) == 1)
    {
        switch (button)
        {
        case 0: return 0;
        case 1:
        {
            scanf("%d", &number);
            for (int i = 0; i < number; i++)
            {
                int a;
                char tmp[100];
                scanf("%s", tmp);
                pname[i] = name[i];
                scanf("%d", &a);
                grade[i] = a;
                strncpy(pname[i], tmp,100);
            }
        }
        break;
        case 2:
            sorted(pname, grade, number);
            break;
        case 3:
        {
            for (int i = 0; i < number; i++)
            {
                printf("%s %d\n", pname[i], grade[i]);
            }
        }
        }
    }
    return 0;
}
void sorted(char* pname[], int* grade, int number)
{
    int a, b;
    for (a = 0; a < number; a++)
    {
        for (b = a + 1; b < number; b++)
        {
            if (grade[a] < grade[b])
            {
                grade[a] = grade[a] ^ grade[b];
                grade[b] = grade[a] ^ grade[b];
                grade[a] = grade[b] ^ grade[a];
                char* tmp;
                tmp = pname[b];
                pname[b] = pname[a];
                pname[a] = tmp;
            }
            if (grade[a] == grade[b])
            {
                if (strncmp(pname[a], pname[b], 100) < 0);
                else
                {
                    grade[a] = grade[a] ^ grade[b];
                    grade[b] = grade[a] ^ grade[b];
                    grade[a] = grade[b] ^ grade[a];
                    char* tmp;
                    tmp = pname[b];
                    pname[b] = pname[a];
                    pname[a] = tmp;
                }
            }
        }
    }
}

strlen()函数

  • 返回值不包括串尾!

str(n)cmp()函数

  • 比较两个字符串
  • 左小右大返回负数,左大右小返回正数,相同返回0。

str(n)cpy()函数

  • 拷贝字符串

atoi()函数

  • int atoi(const char *str) 把参数 str 所指向的字符串转换为一个整数(类型为 int 型)
  • 函数原型:int atoi(const char *str);
  • 头文件:#include
//以下程序输出结果为 1
#define _CRT_SECURE_NO_WARNINGS
#include 
#include 
int main()
{
	char a[] = "-100";
	char b[] = "101";
	int c;
	c = atoi(a) + atoi(b);
	printf("c = %d\n", c);
	return 0;
}

math.h头文件函数

  • fabs函数是一个求绝对值的函数,求出x的绝对值,和数学上的概念相同,函数原型是extern float fabs(float x),用法是#include

stdlib.h头文件函数

malloc()函数使用的误区

解决方法
1.函数返回
  将malloc得到的内存首地址通过函数的返回值返回到主函数。

#include 
#include 
#include 
char* test()
{
    char *p;
    p = (char*)malloc(10 * sizeof(char));
    strcpy(p, "123456789" );
    return p;
}
void main()
{
    char *str = NULL ;
    str = test();
    printf("%s\n", str);
    free(str);
}

2.二级指针
  将malloc得到的内存首地址通过二级指针返回到主函数。

#include 
#include 
#include 
void test(char **p)
{
    *p = (char*)malloc(10 * sizeof(char));
    strcpy(*p, "123456789" );   
}
void main()
{
    char *str = NULL ;
    test(&str);
    printf("%s\n", str);
    free(str);
}

常见误区
1.使用一级指针

#include 
#include 
#include 
void test(char *p) 
{
    p = (char*)malloc(10 * sizeof(char));
    strcpy(*p, "123456789" );   
}
void main()
{
    char *str = NULL ;
    test(str);
    printf("%s\n", str);
    free(str);
}

看上去合情合理,把malloc得的地址赋给指针p,这样我们传入的str就指向申请的内存了。但事实是,str的值并没有变化。我们可以先看下方的代码。

#include 
void test(char c)
{
    c = 'B';
}
void main()
{
    char ch = 'A' ;
    test(ch);
    printf("%c\n", ch);
}

调用test()后,主函数里面的ch值还是’A’,而不是’B’。这是因为在调用函数的时候,char c 事实上是被复制进函数内部的,函数内的操作不会影响到原值。
  指针也是一样的道理。传入一个一级指针,只能修改它指向的数据,而不能修改它指向的地址。所以我们应该传入一个二级指针,这个指针指向一级指针。这样我们就能修改位于二级指针指向的数据,即一级指针指向的地址了。

2.二级指针未指向存在的一级指针

#include 
#include 
#include 
void test(char **p)
{
    *p = (char*)malloc(10 * sizeof(char));
    strcpy(*p, "123456789" );   
}
void main()
{
    char **str = NULL ; //原代码:char *str = NULL;
    test(str);          //       test(&str);
    printf("%s\n", str);
    free(str);
}

为什么我使用了二级指针,仍然是错误的呢?对比下正确的代码,就一目了然了。正确代码中,通过对一级指针str进行取址,得到指向str的二级指针,在子函数中就可以操作str的值了。而错误代码中,二级指针的值为NULL,这样的花子函数中操作的是地址为NULL的内存,这当然是不对的。

友情提醒:记得free()
  molloc申请的内存是位于堆中的,不用后要记得free()掉,否则会造成内存泄漏。

数组

c语言复习备考

C99引入了变长数组,声明数组时方括号内可以用变量,但是变长数组声明时不能进行初始化。

数组名是一种地址常量

字符数组和字符指针变量的区别:

  • 字符数组:由若干个元素组成,每个元素存放一个字符
  • 字符指针变量:存放的是地址,绝不是把字符串放到字符指针变量中。
  • 字符数组内的元素,都是变量,值是可以改变的,重新开辟了一块内存存放变量
  • 字符指针数组内的元素的值并不是变量而是常量,常量不能发生改变

注意:

  • 变长数组可以用变量,但是其声明时不可进行初始化!
  • 多维数组只可以缺省第一维的长度说明
  • 读入字符串到数组中,只需给定数组的首地址!
scanf("%s",&str[0]);

数组常用技巧

常用设置读写头的方法进行修改数组中的元素

#include
#include
void RemoveDuplicate(char* s);
int main()
{
    char str[200];
    while(fgets(str, 200, stdin) != NULL)
    {
   	    RemoveDuplicate(str);
        printf("%s", str);
    }
    return 0;
}

void RemoveDuplicate(char *s)
{
	int r, w, i, len;//r为读头,w为写头!
    len = strlen(s)+1;
    for (r = w = 0; r < len; r++)
    {
        if(s[r] != '\0')
        {
      	    s[w] = s[r];
            for (i = r + 1; i < len; i++)
            {
         	    if(s[i] == s[w])
            	    s[i] = '\0';
            }
            w++;
        }
   }
   s[w] = '\0';
}

运用字符指针数组输入五个字符串存放到另一个数组中

int i;
char* str1[5], str2[5][80];//此处的str1是一个字符型指针数组!
for(i = 0; i<5; i++) str1[i] = str2[i];
for(i = 0; i<5; i++) scanf("%s",str1[i]);

将下标当作需要测试的数,数组的值当作其特征值

#include
#include
#include
static int m, i, k, j, a;
int main()
{
    int *nums;
    nums = (int *)calloc(1000000,sizeof(int));
	scanf("%d", &m);
	for (nums[0] = 0, nums[1] = 0, i = 2; i < m; i++)
	{
		nums[i] = 1;//此处的i是需要测试的数,而其特征值为1表示为其为素数。
	}
	for (j = 2; j < sqrt(m); j++)
	{
		if (nums[j])
		{
			for (k = j * j; k < m; k += j)
			{
				nums[k] = 0;
			}
		}
	}
	for (a = 2; a < m; a++)
	{
		if (nums[a] && nums[a + 2])
		{
			printf("(%d,%d) ", a, a + 2);
		}
	}
	return 0;
}

指针

  • 指针类型的改变不会改变其指向的值得类型
//虽然hdata的指针类型被改为char*,
//但是其指向的值的类型仍然是short类型
#define BYTEO(hdata) (*(char*)&hdata)
#define BYTEI(hdata) (*((char*)&hdata + 1))
	short x = 10, y = 100;
	printf("%hd\n", BYTEO(x));//不可用%c的转换说明!
	printf("%hd\n", BYTEI(y));

指针与数组的细节辨析

  • 如果需要对数组内元素进行修改,采用数组定义而不是指针,因为指针是直接指向其存储区域,而数组是访问其中的副本。
char* p1 = "hanbo";
p1[0] = 'a';//这样修改会引发错误,因为后面所/
//有的相同字符串也会被修改。
printf("hanbo");//此时输出的就可能会用冲突。

指针数组

  • 指针数组的使用时应该先将其指向一个地方,再对其进行赋值。切忌使用野指针。
  • 对指针数组的加法运算。
    • 对数组指针char* s[]进行分析。s是数组名,同时它也是指向s[0]的指针。而s[0]也是一个指向字符的字符型指针。
    • 指针的加法其实就是其指向的位置加一。例如上面的例子,*(s+1) = s[1] , * ( * s+1)=s[0][1]

动态分配内存

  • 函数原型:void* malloc(unsigned size);
  • 使用stdlib.h中的函数malloc()来进行动态分配内存。
  • malloc()函数带有一个参数,为程序员分配多少个字节的空间。注意:这里的单位是字节。而且malloc()返回的是分配的首地址(即void*类型的指针)。所以一般习惯将其强制转换成所需要的类型。
char* s[80];
s[j] = (char*)malloc(strlen(t)+1);

指针进阶

int *pf(float(*a)(int))
//pf是一个函数,返回值为int *指针,参数为一个函数指针
int (*pf(char *))[5]
//pf是一个函数,该函数返回值为指向有5个int元素的数组的指针,参数为char *指针
double (*p[9])(int)
//p是9个元素的指针数组,数组中每个元素是指向有一个整型参数、返回值为双精度浮点型数的函数的指针
char *(*p[3])()
//p是3个元素的函数指针数组,函数指针数组中每个元素所指向的函数是无参字符指针函数

指针实现无符号数移位

void readFloats(const char *fileName){
    FILE *fp;
    fp = fopen(fileName,"rb");
    float nums[10];
    for(int i = 0; i < 10; i++){
    	//注意指这一步操作!
        unsigned bin = *(unsigned *)(nums + i);
        for(int j = 31; j >=0; j--){
            putchar('0'+ ( (bin>>j) &1));
        }
        putchar('\n');
    }  
    fclose(fp);  
}

结构体

  • 定义方法
struct stuff{
        char job[20];
        int age;
        float height;
};
//直接带变量名Huqinwei
struct stuff{
        char job[20];
        int age;
        float height;
}Huqinwei;
  • 声明
   struct stuff Huqinwei = {"manager",30,185};

递归

递归回溯

  • 递归可以用于回到前面的状态,是一种重要的算法思想。
  • 典型例题分析:
    • 迷宫问题。编程找出从入口(左上角方格)经过迷宫到达出口(右下角方格)的所有路径,迷宫问题示意如下方数字阵列所示,0所表示的地方是不可以通行的,只能从1走到与它相邻(四邻域:上、下、左、右相邻)的1上,且不能走重复路径。
    • 迷宫问题示意图:
1 0 0 0 0 0 0 0 0 0
1 0 1 1 0 1 1 1 1 0
1 1 1 0 0 1 1 0 1 0
1 0 0 1 1 1 0 0 1 0
1 1 1 1 0 1 1 1 1 0
0 0 0 0 0 0 0 0 1 1

  • 程序进行走迷宫时,可能会遇到走不通的路,这时需要程序退回到前面的状态——递归的运用
#include
#define N 1000
int mg[N][N], lj[N][N];
int n, m;
void fi(int x, int y);
int main()
{
	int i, j;
	//n行m列的迷宫
	scanf("%d %d", &n, &m);
	for (i = 0; i < n; i++)
		for (j = 0; j < m; j++)
		{
			scanf("%d", &mg[i][j]);
			lj[i][j] = 0;
		}
	//lj存储输出的路径
	lj[0][0] = 1;
	int x = 0, y = 0;
	//递归求解迷宫问题
	fi(x, y);
	return 0;
}

void fi(int x, int y)
{
	if (x == n - 1 && y == m - 1)
	{
		int i, j;
		for (i = 0; i < n; i++)
			for (j = 0; j < m; j++)
			{
				printf("%d", lj[i][j]);
				if (j < m - 1) printf(" ");
				else printf("\n");
			}
	}
	else
	{
		int a, b, i;
		for (i = 1; i < 5; i++)
		{
			switch (i)
			{
			case 1:a = x - 1;
				b = y + 0;
				break;
			case 2:a = x + 1;
				b = y + 0;
				break;
			case 3:a = x + 0;
				b = y - 1;
				break;
			case 4:a = x + 0;
				b = y + 1;
				break;
			}
			if (mg[a][b] == 1 && lj[a][b] == 0)
			{
				lj[a][b] = 1;
				mg[a][b] = 0;
				fi(a, b);
				lj[a][b] = 0;
				mg[a][b] = 1;
			}
		}
	}
}

关于递归与循环的一点思考

  • 循环
    • 当一件事情需要不断重复直到一定条件才能停止时一般使用循环
    • 终止条件简单
    • 一条路走到黑
  • 递归
    • 当一件事需要不断进行,知道某一条件符合时再返回到前面的状态。
    • 可以回到上一状态,更符合人类的思考习惯

文件

feof()函数注意事项

  • 在C语言中,feof()函数的使用是根据指针内容判断的,而非指针位置,无论指针是否到头,甚至超出了,它都需要先读取指针的内容,看一看内容是否是EOF,然后才知道文件到头了。
  • 解决方法:一个原则:先读再判断是否文件结束
ch=fgetc(fp);
while(!feof(fp)){
	printf("%c ASCII: %d\n",ch,ch);
	ch=fgetc(fp);
}

你可能感兴趣的:(c语言)