目錄
1.箭形圖案
思路:?
代碼:
2. 公務員面試
分析:
代碼 :
3. 判斷結構體大小(1)
答案:
分析:
4.判斷結構體大小(2)
答案:
分析:
5.宏定義+計算位段大小的選擇題
分析:?
答案:
6.位段與指針
分析:
答案:
7. 結構體大小判別
分析:?
答案:
8.聯合體的大小
分析:
答案:
9.大小端字節序
分析:
答案:
10.枚舉選擇題
答案:
11. 編程題:找出只出現一次的數字
分析1:
代碼1:
思路2:
代碼2:
12.atoi的實現
13.文件讀寫的選擇題(1)
14.看代碼說功能
15.?文件讀寫的選擇題(2)
16.預處理的選擇題
17.預處理的分析題
18.feof函數的選擇題
19.宏替換的選擇題(1)
20.宏替換的選擇題(2)
21.寫一個宏將一個整數的二進制位的奇數和偶數進行交換
思路:
1.箭形圖案
KiKi學習了循環,BoBo老師給他出了一系列打印圖案的練習,該任務是打印用“*”組成的箭形圖案。
輸入描述:
本題多組輸入,每行一個整數(2~20)。
輸出描述:
針對每行輸入,輸出用“*”組成的箭形。
輸入
2
輸出
*** ******
思路:?
? ? ? ? 可以把圖形分為上下兩部分,如果輸入n上面就是n行,下面就是n+1行;
上半部分:n行
空格:第一行有四個空格,第二行有兩個空格,我們可以把2個空格當做一組,第一行打印兩組,第二行打印一組;每一行需要遞減,所以內部循環減去外面的行數。
*:和行數有關,第一行是一個,第二行是兩個。
下半部分:n+1行
空格:兩個空格為一組,第一行是0組空格,第二行是1組空格,第三行是2組空格,就是行數-1。
*:第一行是3個*,第二行是2個*,第3行是1個*,每次從n+1開始,需要再減去行數。
代碼:
#include<stdio.h>int main()
{int n = 0;while(scanf("%d",&n)){
// 上半部分for (int i = 0; i < n; i++){for (int j = 0; j < n - i; j++){printf(" "); // 先打印空格,兩個為一組}for (int k = 0; k <= i; k++) // 第一行一個*,第二行兩個*,跟行號有關{printf("*");}printf("\n");}// 下半部分for (int i = 0; i < n + 1; i++){// 空格for (int j = 0; j < i; j++){printf(" ");}for (int k = 0; k < n + 1 - i; k++){printf("*");}printf("\n");}}return 0;
}
2. 公務員面試
描述
公務員面試現場打分。有7位考官,從鍵盤輸入若干組成績,每組7個分數(百分制),去掉一個最高分和一個最低分,輸出每組的平均成績。
(注:本題有多組輸入)
輸入描述:
一行,輸入7個整數(0~100),代表7個成績,用空格分隔。
輸出描述:
一行,輸出去掉最高分和最低分的平均成績,小數點后保留2位,每行輸出后換行。
示例1
輸入:
99 45 78 67 72 88 60
輸出:
73.00
分析:
? ? ? ? 本身的邏輯不難,難的是如何oj,我們之前是每次讀取一個數字,我們這道題可以一次讀取一個數字,也能讀取一個數字。
? ? ? ? 若讀取一個數字,我們需要一個變量n來記錄讀取數字的個數,每次讀取n需要++;
? ? ? ? 定義最大最小值并設立處置,每次讀取一個數字就需要判斷是否是最大或者最小值,以此保證最小最大值保持更新,一旦n ==7,說明數字夠了,就開始計算平均值,需要將這些變量進行重新初始化以待下一次實例的調用。
代碼 :
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>int main() {int score = 0;int max = 0;int min = 100; int n = 0;int ret = 0;while (scanf("%d", &score) == 1) {n++;if(score > max){max = score;}if (score < min ) {min = score;}ret += score;if (n == 7) {printf("%.2lf\n",(ret - min - max)/5.0);n = 0;ret = 0;max = 0;min = 100;}}return 0;
}
3. 判斷結構體大小(1)
判斷以下兩個結構體的大小:
#include<stdio.h>struct A
{int a;short b;int c;char d;
};struct B
{int a;short b;char c;int d;
};int main()
{struct A a = { 0 };struct B b = { 0 };printf("%d\n",sizeof(a)); // 16printf("%d\n",sizeof(b)); // 12return 0;
}
答案:
16,12
分析:
A:a占4個字節偏移量0-3;b占2個字節,對齊數是2,偏移量是4-5;c占4個字節,偏移量是4,6不是4的倍數,所以偏移量是8-11;最后一個d是占1個字節,所以偏移量是12;總共占用13個字節,13不是最大對齊數(4)的倍數,那么最近的倍數是16;
B:?a占4個字節偏移量0-3;b占2個字節,對齊數是2,偏移量是4-5;c占1個字節,偏移量是6;d占4個字節,最小對齊數是4,7不是4的倍數,所以偏移量是8,占用8-11;總共占用12個字節,12是最大對齊數的倍數,所以答案是12;
4.判斷結構體大小(2)
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#pragma pack(4) // 4字節對齊
struct S1
{short a; // 0-2char d; // 3long b; // 4-7 long c;// 8-11
};struct S2
{long b;// 0-3short c;// 4-5char d;// 6long a;// 8-11
};
struct S3
{short c;// 0-1long b;// 4-7char d;// 8long a;// 12-15
};
int main()
{printf("%d\n", sizeof(struct S1));printf("%d\n", sizeof(struct S2));printf("%d\n", sizeof(struct S3));}
答案:
12 12 16
分析:
見注釋,這里需要注意的是32位系統重long占4個字節。
5.宏定義+計算位段大小的選擇題
分析:?
①首先需要判斷位段占用的bit位數,首先第一個成員變量開辟一個字節的空間,8bit,第一個成員+第二個成員用了6bit,剩余2bit,此時第三個成員變量需要1個字節,需要單獨開辟,第四個成員變量雖然只需要1bit,但仍然要開辟1字節,總共3字節。
②注意宏定義:3* 2 + 3 = 9
答案:
D
6.位段與指針
分析:
? ? ? ? 首先定義一個char類型的數組,有四個元素;定義一個位段指針指向這個數組;將數組全部初始化為0,;
? ? ? ? 位段的第一個成員是占用1個字節,剩下三個成員占用一個字節,所以這個位段整體占用兩個字節。
? ? ? ? 需要給成員賦值,第一個成員賦值為2,8bit足以存下;
第二個成員只有1bit需要存3,所以只能取低1位bit位;
第三個成員只有2bit需要存4,所以只能取低2位bit位;
第四個成員只有3bit需要存5,剛好可以存下。
此時內存分布如下圖所示:
此時按照16進制打印兩位來輸出每一個字節那么就是:
答案:
0000? 0010 -》 02
0010 1001 -》? 29
0000 0000 -》? 00
0000 0000 -》? 00?
7. 結構體大小判別
分析:?
總大小是10B,最大對齊數是4,所以必須是4的倍數12B。
答案:
12字節?
8.聯合體的大小
union Un
{short s[7];int n;
};
分析:
? ? ? ? 聯合體的奧義是成員公用內存,所以s占用14個字節,n占用4個字節,此時14個字節夠用了,最后需要考慮最大對齊數是4,所以最終應該是4的倍數,16字節。
答案:
16
9.大小端字節序
注:32位cpu平臺
分析:
?首先聯合體是2B,這里分別訪問數組的第一個元素和第二個元素,這里其實就是給兩個字節填充數據;這里需要打印k變量,由于是兩個字節,這里就存在字節序大小端的問題,在vs編譯器中是采用小端存儲,即低字節存在低地址高字節存在高地址(倒著存)那么輸出就是0x3839(還原數據,先打印高地址再打印低地址)
?
答案:
3839?
10.枚舉選擇題
答案:
枚舉從0開始,依次遞增1,中途可以修改,再依次遞增1;
B?
11. 編程題:找出只出現一次的數字
一個數組中有兩個數字出現一次其余數字出現了兩次,找到這兩個只出現了一次的數字。
力扣原題
分析1:
暴力求解,每個元素都要對n個元素進行比較,如果標記到兩個元素相等cnt++,如果cnt是1,那么說明只和自己相等,那就是單獨的數字了。
代碼1:
#include<stdio.h>void find_dog(int arr[], int sz)
{for (int i = 0; i < sz; i++){int cnt = 0;for (int j = 0; j < sz; j++){if (arr[i] == arr[j]){cnt++;}}// cnt = 1的時候需要記錄下來if (cnt == 1) {printf("%d是單獨的數字!\n",arr[i]);}}
}int main()
{int arr[10] = { 1,2,3,4,5,1,2,3,4,6 };int sz = sizeof(arr) / sizeof(arr[0]);find_dog(arr, sz);return 0;
}
思路2:
? ? ? ? 可以利用異或的特性,兩個數字異或如果相同結果就是0,相異結果就是1,所以將這所有的數字全部異或,最后的結果一定不為0,我們假定最后異或出來的結果的最后一位是1,那么相當于最后一位是相異的,那么我們可以按照最后一位是0或者1將所有數字分成兩組,這兩組分別進行異或,就能得到最終相異的數字。
①所有數字異或得到不為0的結果。
②從結果中找到二進制的某一位是1。
③旨在按照此位進行分組,組內進行異或,最后的結果就是其中一個單獨的數字。
核心思想:按照根據異或的原理將兩個單獨的數字分別分為兩組,組內進行異或(其余數字都是成對,異或就是0),最終每組只剩下那個單獨的數字。
代碼2:
/*** Note: The returned array must be malloced, assume caller calls free().*/
int* singleNumber(int* nums, int numsSize, int* returnSize)
{int sum = 0;int* ret = malloc(2 * sizeof(int));int pos = 0;int dog1 = 0;int dog2 = 0;for(int i = 0;i < numsSize;i++){// 1.全部異或得到一個數字sum ^= nums[i];}// 2. 根據這個數字的第n位為1進行分組// 3.計算第n位為1for(int i = 0;i < 32;i ++){if(((sum >> i)&1) == 1){pos = i;break;} }//4. 按照第pos位進行分組,組內進行異或for(int i = 0;i < numsSize;i++){if((nums[i] >> pos)&1 == 1){dog1 ^= nums[i];}else{dog2 ^= nums[i];}}ret[0] = dog1;ret[1] = dog2;*returnSize = 2;return ret;
}
12.atoi的實現
? ? ? ? 即字符串轉換成整數,例如“123456” -》 123456、“-123456” -》 -123456,遇到非數字的時候停止轉化,“-123abc456” -》 -123;
? ? ? ? 需要考慮以下幾點:
①空指針。assert判斷。
②空字符串。字符串只有\0,如果返回0,那就會和“0”產生歧義。
③空格。使用isspace判斷是否是空格,是空格那么str++跳過空格。
④+-號。定義一個正負號的變量,如果遇到+變量置為1,遇到-變量置為-1,最后返回只需要結果*flag即可。
⑤非數字字符。若字符串為123ABC,應該輸出123
正常處理:需要將每一位的數字轉換成整型,存入變量,接下來變量只需要*10 + 新這一位的數字即可。
⑥越界問題:當字符串內的數字非常大,此時就可能發生越界,此時我們需要判斷是正數越界還是負數越界
做到這里,我們可以將簡易版的代碼寫出:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<assert.h>
#include<ctype.h>
#include <limits.h>
// 判斷這個字符串是否合法
enum STATUS
{VALID,INVALID
}status = INVALID;int my_atoi(const char* str)
{// 正負標記int flag = 1;long long ret = 0;assert(str);// 空字符串,返回非法0if (*str == '\0'){return 0;}// 判斷空白字符,跳過while (isspace(*str)){str++;}// 遇到+-號if (*str == '+'){flag = 1;str++;}else if (*str == '-'){flag = -1;str++;}// 正常處理數字字符串while (*str != '\0'){if (isdigit(*str)) {ret = ret * 10 + flag * (*str - '0');// *str是字符,需要-字符'0'轉換成對應的數字// 判斷是否越界if (ret > INT_MAX || ret < INT_MIN) {status = INVALID;return 0;}}else{// 不是數字字符status = INVALID;return str;}str++;}// 沒有提前返回說明是正常處理完畢if(*str == '\0'){status = VALID;}return ret;
}int main()
{char arr[100] = "123456";int ret = my_atoi(arr);if (status == INVALID){printf("數字不合法!:%d\n", ret);}else if (status == VALID){printf("數字為:%d\n", ret);}return 0;
}
測試:
含非數字字符:
?
負數:?
溢出:
?
13.文件讀寫的選擇題(1)
B:getchar適用于標準輸入流。
14.看代碼說功能
統計文件的字符個數?
15.?文件讀寫的選擇題(2)
?
D:sprintf是把格式化的數據寫入字符串中。
16.預處理的選擇題
?C:鏈接階段會查找符號表,看這個函數是否存在。
17.預處理的分析題
判斷變量的類型
#define INT_PTR int*
typedef int* int_ptrINT_PTR a,b;
int_ptr c,d;
替換完畢后是:int *a,b,那么a是int*類型,b是int類型;
下面不一樣,把int*當做一個整體的類型,c,d都是int*類型。
18.feof函數的選擇題
A:錯誤,解析詳見B選項。
19.宏替換的選擇題(1)
答案:70
直接替換計算:?2*(4 + Y(5+1)),Y(5+1)是31。
20.宏替換的選擇題(2)
答案:B?
21.寫一個宏將一個整數的二進制位的奇數和偶數進行交換
思路:
? ? ? ? 假如紅色位置是偶數位,綠色位置是奇數位;將奇數位的數字全部拿出來,向右移動一位;同理將偶數位的數字都拿出來,向左移動一位,最后相加即可。?
? ? ? ? 怎么拿?以偶數位舉例,與一個偶數位都是1,奇數位都是0的32位數字即可。
?
#define SWAP_BIT(n) (((n&0x55555555)<<1) + ((n&0xaaaaaaaa)>>1))int main()
{int n = 10;printf("%d\n", SWAP_BIT(n));return 0;
}
?這里用10進行測試,1010 -> 0101(5)