测试题:
0. 函数名在表达式中应该如何被解读?
答:为函数的地址
答案:函数名可以在表达式中被解读成“指向该函数的指针”。
LU,0-3_4Q1JzEd=Z)S[*
1. 函数指针和指针函数有什么区别?
答:函数指针:为一个指向函数的指针 指针函数:函数的返回值为一个指针
答案:函数指针是一个指向函数的指针;指针函数是一个返回指针变量的函数。
;X!oSFD*qaYgO@5>NP7ZsU`V]AkLyh
2. 一个函数能否有时候返回整型指针,有时候返回字符指针?
答:可以,蒙的
答案:能,定义一个 void * 类型的指针函数。
比如下面代码:
#include <stdio.h>
void *func(int n, int *ptr, char *str);
void *func(int n, int *ptr, char *str)
{
if (n > 0)
{
return ptr;
}
else
{
return str;
}
}
int main(void)
{
int num = 520;
char *str = "FishC";
printf("%d\n", * (int *) (func(1, &num, str)));
printf("%s\n", (char *) func(-1, &num, str));
return 0;
}
程序实现如下:
注:-Wall 选项表示让 GCC 生成所有警告信息(最严格),但编译器仍然对于我们的行为无动于衷。
3. 请问下面代码存在什么问题?
#include <stdio.h>
int *func(int n);
int *func(int n)
{
n++;
return &n;
}
int main(void)
{
printf("%d\n", *func(520));
return 0;
}
答:取址符不能取左值(错误)
答案:上面代码试图返回形参的地址,这跟返回局部变量的地址一样是错误的!因为形参和局部变量在程序运行的时候都是存放在栈中,函数一旦结束调用,栈的空间即释放(虽然不是立刻释放,但其内存空间随时会被覆盖),所以大家跟我一起大声读出来:不要返回局部变量的指针。
4. 有一个指针函数 a,它的返回一个字符串,它有两个参数 b 和 c,其中 b 是一个字符串,c 是一个指向参数为 int 类型,返回值为 void 类型的函数指针。那么……请写出指针函数 a 的声明。
答:char (*a) (char * b, int (*c) (int) );
答案:char *a ( char *b, void (*c) (int) );
5. 请解释下面代码中 func 是个什么东东?
void ( *func ( int, void (*) (int) ) ) (int)
答:指针函数返回的指针,作为函数指针的类型(错误)
答案:解这种题,除非你对指针特别熟练,否则请从最里边的小括号开始分析。
- void (*) (int) 是一个函数指针(没有名字),它指向一个参数为 int 类型,返回值为 void 类型的函数
- func ( int, void(*) (int) ) 可以写成 func (int, 函数指针) 的形式
- func 也是一个函数,它有两个参数,返回值是一个函数指针 void (*) (int)
总结:func 是一个返回值为函数指针(指向一个参数为 int 类型,返回值为 void 类型的函数)的函数,func 有两个参数,第一个参数是 int 类型,第二个参数为函数指针(指向一个参数为 int 类型,返回值为 void 类型的函数)。
注:这道题除了考察你对C语言指针的理解程度之外,几乎没有任何作用,在实际开发中,请不要写这样的代码折腾你的同事……
动动手:
0. 编写一个函数,接收用户输入的字符串,并对该字符串进行 MD5 加密后返回密文。
那……什么是 MD5?MD5 怎么加密?
要求:封装一个 MD5 函数。第一个参数是目标字符串,用于接收运算后的 MD5 值;第二参数是源字符串,即用户输入的明文密码。
程序实现如下:
答:复制答案,运行不了
#include <stdio.h>
#include <string.h>
#include "md5.h"
void md5(unsigned char *dest,unsigned char *src);
void md5(unsigned char *dest,unsigned char *src)
{
int i;
MD5_CTX md5;
//初始化
MD5Init(&md5);
MD5Update(&md5,src,strlen((char*)src));
MD5Final(&md5,dest);
}
int main(void)
{
int i;
unsigned char src[128];
unsigned char dest[16];
printf("请输入密码:");
md5(dest,src);
printf("加密后的密码是:");
for(i=0;i<16;i++)
{
printf("%02x",dest[i]);
}
putchar('\n');
return 0;
}
答案:
#include <stdio.h>
#include <string.h>
#include "md5.h"
void md5(unsigned char *dest, unsigned char *src);
void md5(unsigned char *dest, unsigned char *src)
{
int i;
MD5_CTX md5;
// 初始化
MD5Init(&md5);
// 传入明文字符串及其长度
MD5Update(&md5, src, strlen((char *)src));
// 得到加密后的字符串
MD5Final(&md5, dest);
}
int main(void)
{
int i;
unsigned char src[128];
unsigned char dest[16];
printf("请输入密码:");
scanf("%s", src);
printf("你输入的密码是:%s\n", src);
md5(dest, src);
printf("加密后的密码是:");
for (i = 0; i < 16; i++)
{
printf("%02x", dest[i]);
}
putchar('\n');
return 0;
}
1. MD5 虽然没法逆向,但随着计算机运算能力的提升以及硬盘成本的降低,人们想到了将所有常见的单词组合的 MD5 结果建立数据库以供查询,比较出名的有 md5在线解密破解,md5解密加密 平台,该平台就保存着 24 万亿条加密信息……n
提示1:上一题 MD5 计算出来的值并不是以字节的形式存放(所以我们是 %02x 的形式打印的,记得吗?),所以要将中间 16 位提取出来并加到原始密码的后边,需要大家花点心思琢磨。
提示2:snprintf 函数(中文文档 -> 传送门)或许可以助你一臂之力!
程序实现如下:
答:上一题我都答不出来,这题更是不会
答案:
#include <stdio.h>
#include <string.h>
#include "md5.h"
#define LIMIT 128
void md5(unsigned char *dest, unsigned char *src);
void salt(unsigned char *mix, unsigned char *src);
void md5(unsigned char *dest, unsigned char *src)
{
int i;
MD5_CTX md5;
// 初始化
MD5Init(&md5);
// 传入明文字符串及其长度
MD5Update(&md5, src, strlen((char *)src));
// 得到加密后的字符串
MD5Final(&md5, dest);
}
void salt(unsigned char *mix, unsigned char *src)
{
unsigned char temp1[16]; // 存储原始数据第一次计算MD5的值
unsigned char temp2[16]; // 存储temp1中间16位为字符串
int i, j, length;
md5(temp1, src);
for (i = 4, j = 0; i < 12; i++, j++)
{
snprintf(temp2 + 2*j, 16, "%02x", temp1[i]);
}
length = strlen(src);
strncpy(mix, src, length);
strncat(mix, temp2, 16);
mix[length + 16 + 1] = '\0';
}
int main(void)
{
int i;
unsigned char src[LIMIT];
unsigned char mix[LIMIT + 16];
unsigned char dest[16];
printf("请输入密码:");
scanf("%s", src);
printf("你输入的密码是:%s\n", src);
salt(mix, src);
printf("加盐后的密码是:%s\n", mix);
md5(dest, mix);
printf("加密后的密码是:");
for (i = 0; i < 16; i++)
{
printf("%02x", dest[i]);
}
putchar('\n');
return 0;
}
2. 请编写一个程序,要求用户输入两个数,分别计算它们加减乘除的结果。
请确保你的代码符合下面要求:2
- 加减乘除分别由四个函数实现:add、sub、mul 和 divi
- 定义一个函数指针数组指向上面四个函数
- 只能通过函数指针的形式访问上面四个函数
- 需要支持浮点数,注意除数不能为 0
程序实现如下:
答:很有代表性的一道题,我的方法也可以
#include <stdio.h>
float add(float num1,float num2)
{
return (num1+num2);
}
float sub(float num1,float num2)
{
return (num1-num2);
}
float mul(float num1,float num2)
{
return (num1 * num2);
}
float divi(float num1,float num2)
{
if(num2 == 0){
return 0;
}
else{
return (num1/num2);
}
}
float calc(float (*fp)(float, float), float num1, float num2)
{
return (*fp)(num1,num2);
}
int main()
{
float num1,num2;
printf("请输入两个:");
scanf("%f %f",&num1,&num2);
printf("对这两个数加减乘除后的结果是:%.2f %.2f %.2f %.2f",
calc(add,num1,num2),calc(sub,num1,num2),calc(mul,num1,num2),calc(divi,num1,num2));
}
答案:
#include <stdio.h>
#include <stdlib.h>
#define EPSINON 0.000001 // 定义允许的误差
double add(double x, double y);
double sub(double x, double y);
double mul(double x, double y);
double divi(double x, double y);
double add(double x, double y)
{
return x + y;
}
double sub(double x, double y)
{
return x - y;
}
double mul(double x, double y)
{
return x * y;
}
double divi(double x, double y)
{
// 不要对浮点数进行==或!=比较,因为IEEE浮点数是一个近似值
if (y >= -EPSINON && y <= EPSINON)
{
printf("除数不能为0\n");
// 如果除数为0,调用exit()函数直接退出程序
exit(1);
}
else
{
return x / y;
}
}
int main(void)
{
int i;
double x, y, result;
double (*func_table[4])(double, double) = {add, sub, mul, divi};
printf("请输入两个数:");
scanf("%lf %lf", &x, &y);
printf("对这两个数进行加减乘除后的结果是:");
for (i = 0; i < 4; i++)
{
result = (*func_table[i])(x, y);
printf("%.2f ", result);
}
putchar('\n');
return 0;
}