merge branch 'dev' to 'main' (#1)

* feat(shiki): switch to official transformers and add error/warning support
* feat: add article `c-quick-start`
* feat: add article `c-pointers-and-structs`
* chore: update `css-flex-layout` post thumbnail
This commit is contained in:
awfufu
2025-12-12 12:16:26 +08:00
committed by GitHub
parent b1c3db9033
commit 6dd7ccff8c
16 changed files with 1246 additions and 175 deletions

View File

@@ -7,6 +7,7 @@ import rehypeKatex from 'rehype-katex'
import remarkMath from 'remark-math'
// Local integrations
import { remarkAside } from './src/plugins/remark-aside.ts'
// Local rehype & remark plugins
import rehypeAutolinkHeadings from './src/plugins/rehype-auto-link-headings.ts'
// Shiki
@@ -16,9 +17,12 @@ import {
addTitle,
transformerNotationDiff,
transformerNotationHighlight,
transformerNotationFocus,
transformerNotationErrorLevel,
updateStyle
} from './src/plugins/shiki-transformers.ts'
import config from './src/site.config.ts'
import remarkDirective from 'remark-directive'
// https://astro.build/config
export default defineConfig({
@@ -67,7 +71,7 @@ export default defineConfig({
},
// Markdown Options
markdown: {
remarkPlugins: [remarkMath],
remarkPlugins: [remarkDirective, remarkAside, remarkMath],
rehypePlugins: [
[rehypeKatex, {}],
rehypeHeadingIds,
@@ -87,8 +91,10 @@ export default defineConfig({
dark: 'dark-plus'
},
transformers: [
transformerNotationDiff(),
transformerNotationHighlight(),
transformerNotationDiff() as any,
transformerNotationHighlight() as any,
transformerNotationFocus() as any,
transformerNotationErrorLevel() as any,
updateStyle(),
addTitle(),
addLanguage(),

View File

@@ -8,6 +8,7 @@
"@astrojs/check": "^0.9.5",
"@astrojs/cloudflare": "^12.6.12",
"@astrojs/rss": "^4.0.13",
"@shikijs/transformers": "^3.19.0",
"@waline/client": "^3.7.1",
"astro": "^5.15.3",
"astro-pure": "1.3.6",
@@ -24,6 +25,9 @@
"eslint-plugin-astro": "^1.5.0",
"prettier": "^3.6.2",
"prettier-plugin-astro": "^0.14.1",
"remark-directive": "^4.0.0",
"toggle-selection": "^1.0.6",
"unist-util-visit": "^5.0.0",
},
},
"packages/pure": {
@@ -352,7 +356,7 @@
"@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.52.5", "", { "os": "win32", "cpu": "x64" }, "sha512-TAcgQh2sSkykPRWLrdyy2AiceMckNf5loITqXxFI5VuQjS5tSuw3WlwdN8qv8vzjLAUTvYaH/mVjSFpbkFbpTg=="],
"@shikijs/core": ["@shikijs/core@3.15.0", "", { "dependencies": { "@shikijs/types": "3.15.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-8TOG6yG557q+fMsSVa8nkEDOZNTSxjbbR8l6lF2gyr6Np+jrPlslqDxQkN6rMXCECQ3isNPZAGszAfYoJOPGlg=="],
"@shikijs/core": ["@shikijs/core@3.19.0", "", { "dependencies": { "@shikijs/types": "3.19.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-L7SrRibU7ZoYi1/TrZsJOFAnnHyLTE1SwHG1yNWjZIVCqjOEmCSuK2ZO9thnRbJG6TOkPp+Z963JmpCNw5nzvA=="],
"@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.15.0", "", { "dependencies": { "@shikijs/types": "3.15.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.3" } }, "sha512-ZedbOFpopibdLmvTz2sJPJgns8Xvyabe2QbmqMTz07kt1pTzfEvKZc5IqPVO/XFiEbbNyaOpjPBkkr1vlwS+qg=="],
@@ -362,7 +366,9 @@
"@shikijs/themes": ["@shikijs/themes@3.15.0", "", { "dependencies": { "@shikijs/types": "3.15.0" } }, "sha512-8ow2zWb1IDvCKjYb0KiLNrK4offFdkfNVPXb1OZykpLCzRU6j+efkY+Y7VQjNlNFXonSw+4AOdGYtmqykDbRiQ=="],
"@shikijs/types": ["@shikijs/types@3.15.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-BnP+y/EQnhihgHy4oIAN+6FFtmfTekwOLsQbRw9hOKwqgNy8Bdsjq8B05oAt/ZgvIWWFrshV71ytOrlPfYjIJw=="],
"@shikijs/transformers": ["@shikijs/transformers@3.19.0", "", { "dependencies": { "@shikijs/core": "3.19.0", "@shikijs/types": "3.19.0" } }, "sha512-e6vwrsyw+wx4OkcrDbL+FVCxwx8jgKiCoXzakVur++mIWVcgpzIi8vxf4/b4dVTYrV/nUx5RjinMf4tq8YV8Fw=="],
"@shikijs/types": ["@shikijs/types@3.19.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-Z2hdeEQlzuntf/BZpFG8a+Fsw9UVXdML7w0o3TgSXV3yNESGon+bs9ITkQb3Ki7zxoXOOu5oJWqZ2uto06V9iQ=="],
"@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="],
@@ -942,6 +948,8 @@
"mdast-util-definitions": ["mdast-util-definitions@6.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "unist-util-visit": "^5.0.0" } }, "sha512-scTllyX6pnYNZH/AIp/0ePz6s4cZtARxImwoPJ7kS42n+MnVsI4XbnG6d4ibehRIldYMWM2LD7ImQblVhUejVQ=="],
"mdast-util-directive": ["mdast-util-directive@3.1.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0", "parse-entities": "^4.0.0", "stringify-entities": "^4.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-I3fNFt+DHmpWCYAT7quoM6lHf9wuqtI+oCOfvILnoicNIqjh5E3dEJWiXuYME2gNe8vl1iMQwyUHa7bgFmak6Q=="],
"mdast-util-find-and-replace": ["mdast-util-find-and-replace@3.0.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "escape-string-regexp": "^5.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg=="],
"mdast-util-from-markdown": ["mdast-util-from-markdown@2.0.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "mdast-util-to-string": "^4.0.0", "micromark": "^4.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA=="],
@@ -984,6 +992,8 @@
"micromark-core-commonmark": ["micromark-core-commonmark@2.0.3", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-destination": "^2.0.0", "micromark-factory-label": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-factory-title": "^2.0.0", "micromark-factory-whitespace": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-html-tag-name": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg=="],
"micromark-extension-directive": ["micromark-extension-directive@4.0.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-factory-whitespace": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "parse-entities": "^4.0.0" } }, "sha512-/C2nqVmXXmiseSSuCdItCMho7ybwwop6RrrRPk0KbOHW21JKoCldC+8rFOaundDoRBUWBnJJcxeA/Kvi34WQXg=="],
"micromark-extension-gfm": ["micromark-extension-gfm@3.0.0", "", { "dependencies": { "micromark-extension-gfm-autolink-literal": "^2.0.0", "micromark-extension-gfm-footnote": "^2.0.0", "micromark-extension-gfm-strikethrough": "^2.0.0", "micromark-extension-gfm-table": "^2.0.0", "micromark-extension-gfm-tagfilter": "^2.0.0", "micromark-extension-gfm-task-list-item": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w=="],
"micromark-extension-gfm-autolink-literal": ["micromark-extension-gfm-autolink-literal@2.1.0", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw=="],
@@ -1190,6 +1200,8 @@
"rehype-stringify": ["rehype-stringify@10.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-to-html": "^9.0.0", "unified": "^11.0.0" } }, "sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA=="],
"remark-directive": ["remark-directive@4.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-directive": "^3.0.0", "micromark-extension-directive": "^4.0.0", "unified": "^11.0.0" } }, "sha512-7sxn4RfF1o3izevPV1DheyGDD6X4c9hrGpfdUpm7uC++dqrnJxIZVkk7CoKqcLm0VUMAuOol7Mno3m6g8cfMuA=="],
"remark-gfm": ["remark-gfm@4.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-gfm": "^3.0.0", "micromark-extension-gfm": "^3.0.0", "remark-parse": "^11.0.0", "remark-stringify": "^11.0.0", "unified": "^11.0.0" } }, "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg=="],
"remark-math": ["remark-math@6.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-math": "^3.0.0", "micromark-extension-math": "^3.0.0", "unified": "^11.0.0" } }, "sha512-MMqgnP74Igy+S3WwnhQ7kqGlEerTETXMvJhrUzDikVZ2/uogJCb+WHUg97hK9/jcfc0dkD73s3LN8zU49cTEtA=="],
@@ -1292,6 +1304,8 @@
"to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="],
"toggle-selection": ["toggle-selection@1.0.6", "", {}, "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ=="],
"totalist": ["totalist@3.0.1", "", {}, "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ=="],
"trim-lines": ["trim-lines@3.0.1", "", {}, "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg=="],
@@ -1476,6 +1490,14 @@
"@rollup/pluginutils/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
"@shikijs/engine-javascript/@shikijs/types": ["@shikijs/types@3.15.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-BnP+y/EQnhihgHy4oIAN+6FFtmfTekwOLsQbRw9hOKwqgNy8Bdsjq8B05oAt/ZgvIWWFrshV71ytOrlPfYjIJw=="],
"@shikijs/engine-oniguruma/@shikijs/types": ["@shikijs/types@3.15.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-BnP+y/EQnhihgHy4oIAN+6FFtmfTekwOLsQbRw9hOKwqgNy8Bdsjq8B05oAt/ZgvIWWFrshV71ytOrlPfYjIJw=="],
"@shikijs/langs/@shikijs/types": ["@shikijs/types@3.15.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-BnP+y/EQnhihgHy4oIAN+6FFtmfTekwOLsQbRw9hOKwqgNy8Bdsjq8B05oAt/ZgvIWWFrshV71ytOrlPfYjIJw=="],
"@shikijs/themes/@shikijs/types": ["@shikijs/types@3.15.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-BnP+y/EQnhihgHy4oIAN+6FFtmfTekwOLsQbRw9hOKwqgNy8Bdsjq8B05oAt/ZgvIWWFrshV71ytOrlPfYjIJw=="],
"@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
"@unocss/cli/chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="],
@@ -1526,6 +1548,10 @@
"prompts/kleur": ["kleur@3.0.3", "", {}, "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="],
"shiki/@shikijs/core": ["@shikijs/core@3.15.0", "", { "dependencies": { "@shikijs/types": "3.15.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-8TOG6yG557q+fMsSVa8nkEDOZNTSxjbbR8l6lF2gyr6Np+jrPlslqDxQkN6rMXCECQ3isNPZAGszAfYoJOPGlg=="],
"shiki/@shikijs/types": ["@shikijs/types@3.15.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-BnP+y/EQnhihgHy4oIAN+6FFtmfTekwOLsQbRw9hOKwqgNy8Bdsjq8B05oAt/ZgvIWWFrshV71ytOrlPfYjIJw=="],
"sitemap/@types/node": ["@types/node@17.0.45", "", {}, "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw=="],
"vscode-json-languageservice/jsonc-parser": ["jsonc-parser@3.3.1", "", {}, "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ=="],

View File

@@ -22,6 +22,7 @@
"@astrojs/check": "^0.9.5",
"@astrojs/cloudflare": "^12.6.12",
"@astrojs/rss": "^4.0.13",
"@shikijs/transformers": "^3.19.0",
"@waline/client": "^3.7.1",
"astro": "^5.15.3",
"astro-pure": "1.3.6",
@@ -42,7 +43,10 @@
"eslint": "^9.39.1",
"eslint-plugin-astro": "^1.5.0",
"prettier": "^3.6.2",
"prettier-plugin-astro": "^0.14.1"
"prettier-plugin-astro": "^0.14.1",
"remark-directive": "^4.0.0",
"toggle-selection": "^1.0.6",
"unist-util-visit": "^5.0.0"
},
"workspaces": [
"packages/*"

View File

@@ -5,12 +5,14 @@
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Satoshi';
src: url('/fonts/Satoshi-VariableItalic.ttf') format('truetype');
font-style: italic;
font-display: swap;
}
html {
font-family: 'Satoshi', sans-serif;
}
@@ -38,6 +40,7 @@ html {
--ring: 240 5.9% 10%;
--radius: 0.5rem;
}
.dark {
--background: 240 20.54% 5.2%;
--foreground: 0 0% 98%;
@@ -59,9 +62,11 @@ html {
--input: 240 3.7% 15.9%;
--ring: 240 4.9% 83.9%;
}
:root {
--un-default-border-color: hsl(var(--border) / 1);
}
html.dark {
color-scheme: dark;
}
@@ -69,7 +74,139 @@ html.dark {
/* Global */
a {
transition: color 0.2s ease;
&:hover {
color: hsl(var(--primary) / var(--un-text-opacity, 1));
}
}
/* Aside Component Styles (Remark Integration) */
.aside>.aside-container {
--un-bg-opacity: 0.07;
}
.aside>.aside-container.aside-tip {
--primary: 234 60% 60%;
}
.aside>.aside-container.aside-caution {
--primary: 41 90% 50%;
}
.aside>.aside-container.aside-danger {
--primary: 339 90% 60%;
}
.aside>.aside-container .aside-content> :first-child {
margin-top: 0;
}
.aside>.aside-container .aside-content> :last-child {
margin-bottom: 0;
}
.aside .icon {
width: 1.5em;
height: 1.5em;
display: inline-block;
vertical-align: middle;
}
/* Focus Transformer */
.has-focused .line:not(.focused) {
transition: opacity 0.35s;
}
.has-focused .line:not(.focused)>span {
opacity: 0.4;
filter: blur(0.095rem);
transition:
filter 0.35s,
opacity 0.35s;
}
.has-focused:hover .line:not(.focused) {
opacity: 1;
}
.has-focused:hover .line:not(.focused)>span {
opacity: 1;
filter: blur(0);
}
/* Error Transformer */
.has-error .line:not(.error) {
transition: opacity 0.35s;
}
.has-error .line:not(.error)>span {
opacity: 0.4;
filter: blur(0.095rem);
transition:
filter 0.35s,
opacity 0.35s;
}
.has-error:hover .line:not(.error) {
opacity: 1;
}
.has-error:hover .line:not(.error)>span {
opacity: 1;
filter: blur(0);
}
.line.error {
background-color: rgba(244, 63, 94, 0.1);
/* Rose-500 optimized for dark/light */
border-left: 2px solid rgb(244, 63, 94);
}
/* Warning Transformer */
.has-warning .line:not(.warning) {
transition: opacity 0.35s;
}
.has-warning .line:not(.warning)>span {
opacity: 0.4;
filter: blur(0.095rem);
transition:
filter 0.35s,
opacity 0.35s;
}
.has-warning:hover .line:not(.warning) {
opacity: 1;
}
.has-warning:hover .line:not(.warning)>span {
opacity: 1;
filter: blur(0);
}
.line.warning {
background-color: rgba(234, 179, 8, 0.1);
border-left: 2px solid rgb(234, 179, 8);
}
.line.highlighted.error {
background-color: rgba(244, 63, 94, 0.1) !important;
border-left: 2px solid rgb(244, 63, 94) !important;
}
.line.highlighted.warning {
background-color: rgba(234, 179, 8, 0.1) !important;
border-left: 2px solid rgb(234, 179, 8) !important;
}
.line.highlighted.error::before,
.line.error::before {
background-color: transparent !important;
color: inherit !important;
}
.line.highlighted.warning::before,
.line.warning::before {
background-color: transparent !important;
color: inherit !important;
}

View File

@@ -0,0 +1,79 @@
<div class='pointer-diagram my-8 flex justify-center'>
<svg width='600' height='200' viewBox='0 0 600 200' class='font-sans'>
<g transform='translate(320, 60)'>
<rect
x='0'
y='0'
width='210'
height='60'
rx='4'
class='fill-card stroke-border'
stroke-width='2'></rect>
<text x='105' y='35' text-anchor='middle' class='fill-foreground text-xl font-bold'>20</text>
<text x='105' y='-10' text-anchor='middle' class='fill-muted-foreground text-sm font-mono'
>var (int)</text
>
<text x='105' y='80' text-anchor='middle' class='fill-muted-foreground text-xs font-mono'
>0x7ffe56d638dc</text
>
</g>
<g transform='translate(40, 60)'>
<rect
x='0'
y='0'
width='210'
height='60'
rx='4'
class='fill-card stroke-border'
stroke-width='2'></rect>
<text x='105' y='35' text-anchor='middle' class='fill-foreground text-sm font-mono'
>0x7ffe56d638dc</text
>
<text x='105' y='-10' text-anchor='middle' class='fill-muted-foreground text-sm font-mono'
>p (int*)</text
>
<text x='105' y='80' text-anchor='middle' class='fill-muted-foreground text-xs font-mono'
>0x7ffe56d638e0</text
>
</g>
<path
d='M 250 90 L 312 90'
fill='none'
class='stroke-primary'
stroke-width='2'
marker-end='url(#arrowhead)'></path>
<defs>
<marker id='arrowhead' markerWidth='10' markerHeight='7' refX='9' refY='3.5' orient='auto'>
<polygon points='0 0, 10 3.5, 0 7' class='fill-primary'></polygon>
</marker>
</defs>
</svg>
</div>
<style>
.fill-card {
fill: hsl(var(--card));
}
.stroke-border {
stroke: hsl(var(--border));
}
.fill-foreground {
fill: hsl(var(--foreground));
}
.fill-muted-foreground {
fill: hsl(var(--muted-foreground));
}
.stroke-primary {
stroke: hsl(var(--primary));
}
.fill-primary {
fill: hsl(var(--primary));
}
.font-sans {
font-family: var(--font-sans, ui-sans-serif, system-ui, sans-serif);
}
.font-mono {
font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace);
}
</style>

View File

@@ -0,0 +1,129 @@
<div class='struct-memory-layout my-8 flex justify-center overflow-x-auto'>
<svg width='600' height='120' viewBox='0 0 600 120' class='font-sans'>
<g transform='translate(100, 40)'>
<g>
<rect x='0' y='0' width='50' height='50' class='fill-card stroke-border' stroke-width='2'
></rect>
<text x='25' y='30' text-anchor='middle' class='fill-foreground text-sm font-mono'>c</text>
<text x='25' y='-10' text-anchor='middle' class='fill-muted-foreground text-xs font-mono'
>0</text
>
</g>
<g transform='translate(50, 0)'>
<rect
x='0'
y='0'
width='50'
height='50'
class='fill-card stroke-muted-foreground'
stroke-width='1'
stroke-dasharray='4 4'></rect>
<text x='25' y='30' text-anchor='middle' class='fill-muted-foreground text-xs font-mono'
>pad</text
>
<text x='25' y='-10' text-anchor='middle' class='fill-muted-foreground text-xs font-mono'
>1</text
>
</g>
<g transform='translate(100, 0)'>
<rect
x='0'
y='0'
width='50'
height='50'
class='fill-card stroke-muted-foreground'
stroke-width='1'
stroke-dasharray='4 4'></rect>
<text x='25' y='30' text-anchor='middle' class='fill-muted-foreground text-xs font-mono'
>pad</text
>
<text x='25' y='-10' text-anchor='middle' class='fill-muted-foreground text-xs font-mono'
>2</text
>
</g>
<g transform='translate(150, 0)'>
<rect
x='0'
y='0'
width='50'
height='50'
class='fill-card stroke-muted-foreground'
stroke-width='1'
stroke-dasharray='4 4'></rect>
<text x='25' y='30' text-anchor='middle' class='fill-muted-foreground text-xs font-mono'
>pad</text
>
<text x='25' y='-10' text-anchor='middle' class='fill-muted-foreground text-xs font-mono'
>3</text
>
</g>
<g transform='translate(200, 0)'>
<rect x='0' y='0' width='50' height='50' class='fill-card stroke-border' stroke-width='2'
></rect>
<text x='25' y='30' text-anchor='middle' class='fill-foreground text-sm font-mono'>i</text>
<text x='25' y='-10' text-anchor='middle' class='fill-muted-foreground text-xs font-mono'
>4</text
>
<rect x='50' y='0' width='50' height='50' class='fill-card stroke-border' stroke-width='2'
></rect>
<text x='75' y='30' text-anchor='middle' class='fill-foreground text-sm font-mono'>i</text>
<text x='75' y='-10' text-anchor='middle' class='fill-muted-foreground text-xs font-mono'
>5</text
>
<rect x='100' y='0' width='50' height='50' class='fill-card stroke-border' stroke-width='2'
></rect>
<text x='125' y='30' text-anchor='middle' class='fill-foreground text-sm font-mono'>i</text>
<text x='125' y='-10' text-anchor='middle' class='fill-muted-foreground text-xs font-mono'
>6</text
>
<rect x='150' y='0' width='50' height='50' class='fill-card stroke-border' stroke-width='2'
></rect>
<text x='175' y='30' text-anchor='middle' class='fill-foreground text-sm font-mono'>i</text>
<text x='175' y='-10' text-anchor='middle' class='fill-muted-foreground text-xs font-mono'
>7</text
>
</g>
</g>
<text x='125' y='110' text-anchor='middle' class='fill-foreground text-sm font-bold'
>char (1B)</text
>
<text x='400' y='110' text-anchor='middle' class='fill-foreground text-sm font-bold'
>int (4B)</text
>
</svg>
</div>
<style>
.fill-card {
fill: hsl(var(--card));
}
.stroke-border {
stroke: hsl(var(--border));
}
.stroke-muted-foreground {
stroke: hsl(var(--muted-foreground));
}
.fill-foreground {
fill: hsl(var(--foreground));
}
.fill-muted-foreground {
fill: hsl(var(--muted-foreground));
}
.stroke-primary {
stroke: hsl(var(--primary));
}
.fill-primary {
fill: hsl(var(--primary));
}
.font-sans {
font-family: var(--font-sans, ui-sans-serif, system-ui, sans-serif);
}
.font-mono {
font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace);
}
</style>

View File

@@ -0,0 +1,502 @@
---
title: C指针和结构体
publishDate: 2022-6-11 21:01:00
description: '指针是C语言的精髓'
tags:
- C语言
heroImage: { src: './thumbnail.webp' } # source: https://www.pexels.com/photo/close-up-og-pins-in-a-world-map-8828584/
language: 'zh-cn'
---
:::tip
阅读本文之前,读者应该拥有至少一门其他的编程语言基础,了解少许的 Linux 命令。
:::
import { Card } from 'astro-pure/user'
<Card as='a' href='/blog/c-quick-start' heading='上一章'
subheading='基础语法'>C语言快速入门</Card>
## 指针类型
### 什么是指针
指针是 C 语言的精髓,指针也是一种变量,可以存储变量在内存中的地址。
- `&`: 取地址运算符
- `*`: 寻址运算符(解引用)
```c
#include <stdio.h>
int main() {
int var = 20;
int* p = &var; // * 表示这是一个 int 类型的指针,将 var 的地址赋值给 p
printf("&var: %p\n", &var); // %p 打印地址
// &var: 0x7ffe56d638dc
printf("p: %p\n", p);
// p: 0x7ffe56d638dc
printf("*p: %d\n", *p);
// *p: 20
return 0;
}
```
这里的指针变量 `p` 存储了 `var` 变量所在的内存块的地址。使用 `*p` 可以从这个地址访问 `var`,并获取 `var` 的值。
import PointerDiagram from './PointerDiagram.astro';
<PointerDiagram />
通过指针,我们可以直接访问和操作内存,这使得 C 语言非常强大且高效,但也需要小心使用以避免错误(如空指针、野指针)。
### 空指针和野指针
字面意思,空指针就是指向空地址(地址为零)的指针;野指针就是指向无意义地址或非法地址的指针。
在 `stddef.h` 头文件中,定义了 `NULL`,它可以用来表示空指针。
```c
#define NULL ((void *)0)
```
```c
int* p1 = NULL; // 空指针指向0
int* p2; // 野指针:未初始化,内容随机
```
这种指针要小心使用,一旦对它们进行解引用操作,就会导致未定义行为。比如访问 `*p1` 时,程序会尝试访问零地址处的内存,这通常会导致程序崩溃。
```c
#include <stdio.h>
int main() {
int* p = NULL;
printf("%d\n", *p); // segmentation fault // [!code error]
return 0;
}
```
在访问指针之前,必须小心谨慎,确保指针被正确地初始化了且不为空。
```c
#include <stdio.h>
int main() {
int a = 10;
int* p = &a;
if (!p) { // [!code ++]
printf("p is null!\n"); // [!code ++]
return 1; // [!code ++]
} // [!code ++]
printf("%d\n", *p);
return 0;
}
```
### 指针的大小
指针本身也是个变量,所以它也需要占用内存空间来存储。在 32 位bit系统上指针的大小是 4 字节,在 64 位系统上则是 8 字节。这里的“位”指的是地址总线的位数,现代机器通常是 64 位。
在 64 位机器上,可以给 `gcc` 命令添加 `-m32` 参数来生成 32 位的可执行文件进行测试。
```c
#include <stdio.h>
int main() {
int* p = NULL;
printf("sizeof(p): %zu\n", sizeof(p));
return 0;
}
```
在 32 位系统上:
```sh
gcc main.c -m32 -o main
./main
# sizeof(p): 4
```
在 64 位系统上:
```sh
gcc main.c -m64 -o main
./main
# sizeof(p): 8
```
### 指针的作用
在函数内部定义的变量称为局部变量。它们通常存储在栈Stack其生命周期仅限于函数执行期间。当函数执行完后这些变量会被销毁内存会被回收。
```c
#include <stdio.h>
void foo(int x) {
x = 100; // 修改数值 // [!code hl]
printf("foo: x = %d\n", x);
}
int main() {
int x = 10;
foo(x);
printf("main: x = %d\n", x);
return 0;
}
// Output:
// foo: x = 100
// main: x = 10
```
因为 C 语言是按值传递Pass by Value直接传递变量只会拷贝一份副本。如果想在函数内部修改函数外部定义的变量则需要传递变量的地址指针这样函数就可以通过地址直接操作原始数据。
```c title="main.c"
#include <stdio.h>
// 交换两个整数的值 使用指针
void swap(int* a, int* b) {
int temp = *a;
*a = *b;
*b = temp;
}
int main() {
int x = 5, y = 10;
printf("Before: x = %d, y = %d\n", x, y);
swap(&x, &y); // 传递地址 // [!code hl]
printf("After: x = %d, y = %d\n", x, y);
return 0;
}
```
## 数组和指针
### 数组名即指针
数组名在大多数表达式中会“退化”为指向数组第一个元素的指针。
```c
int arr[5] = {10, 20, 30, 40, 50};
int* p = arr; // = &arr[0]
printf("p: %p\n", p);
printf("arr: %p\n", arr);
printf("&arr[0]: %p\n", &arr[0]);
// Output:
// p: 0x7ffe56d638dc
// arr: 0x7ffe56d638dc
// &arr[0]: 0x7ffe56d638dc
```
在上面的示例中,`p` 的值等于 `arr` 的值等于 `&arr[0]` 的值。
```c
printf("arr[0] = %d\n", arr[0]);
printf("*p = %d\n", *p);
// Output:
// arr[0] = 10
// *p = 10
```
`arr[0]` 和 `*p` 都可以访问数组的第一个元素。
### 指针的运算
指针可以进行加减运算,加法运算会将指针移动到下一个元素的位置,减法运算会将指针移动到前一个元素的位置。
```c
printf("arr[1] = %d\n", arr[1]);
printf("*(p + 1) = %d\n", *(p + 1));
printf("p[1] = %d\n", p[1]);
// Output:
// arr[1] = 20
// *(p + 1) = 20
// p[1] = 20
```
对 int 类型的指针 `p` 执行 `p + 1` 会将指针移动到下一个 int 的位置。指针 `p` 也可以当作一个 int 数组进行下标访问,这里的 `p[1]` 等价于 `*(p + 1)`。
你可以尝试不同类型的指针,比如 `char*`,你会发现指针移动的步长是 1。
```c
char arr[] = {'a', 'b', 'c', 'd', 'e'};
char* p = arr;
printf("p: %p\n", p);
printf("p + 1: %p\n", p + 1);
printf("p - 1: %p\n", p - 1);
```
### 字符串
之前说过,字符串实际上就是字符数组,并且以空字符 `\0` 结尾。
```c
char str[] = "hello";
char* p = str;
printf("str: %s, p: %s\n", str, p);
// str: hello, p: hello
p[0] = 'H';
printf("str: %s, p: %s\n", str, p);
// str: Hello, p: Hello
```
### 操作字符串
C 标准库提供了一些用于操作字符串的函数,这些函数都定义在 `string.h` 头文件中。
```c
#include <stdio.h>
#include <string.h>
int main() {
char str1[10] = "hello";
char str2[10];
printf("strlen(str1): %zu\n", strlen(str1));
strcpy(str2, str1);
printf("str2: %s\n", str2);
return 0;
}
```
使用指针,我们可以高效地实现 `strcpy` 函数。
```
char* strcpy(char* dest, const char* src) {
char* p = dest;
while (*dest++ = *src++);
return p;
}
```
你可以通过 [string.h 文档](https://en.cppreference.com/w/c/header/string.html) 学习更多接口。
## 函数指针
### 函数名即指针
就像数组名是数组第一个元素地址的指针一样,函数名也是指向函数地址的指针。我们可以定义一个指针变量来存储函数的地址,这就是函数指针。
```c
#include <stdio.h>
void say_hello() {
printf("Hello World!\n");
}
int add(int a, int b) {
return a + b;
}
int main() {
// 定义指向 say_hello 的指针
void (*func_ptr)(); // [!code hl]
func_ptr = say_hello;
// 通过指针调用函数
func_ptr(); // 或 (*func_ptr)(); // [!code hl]
// 定义指向 add 的指针
int (*add_ptr)(int, int) = add;
printf("2 + 3 = %d\n", add_ptr(2, 3));
return 0;
}
```
### 回调函数
不像其他高级语言C 语言没有类似 lambda 表达式或闭包的语法糖,但是我们可以通过函数指针来实现类似的功能。
函数指针的一个主要用途是实现回调函数Callback Function。回调函数是指将一个函数指针作为参数传递给另一个函数由另一个函数在适当的时候调用这个函数。
```c
#include <stdio.h>
int add(int a, int b) { return a + b; }
int sub(int a, int b) { return a - b; }
// 接受函数指针作为参数
void compute(int (*op)(int, int), int a, int b) {
int result = op(a, b);
printf("Result: %d\n", result);
}
int main() {
compute(add, 10, 5); // Result: 15
compute(sub, 10, 5); // Result: 5
return 0;
}
```
C 语言标准库中的 `qsort` 就是一个典型的例子,它允许用户自定义比较逻辑。
```c
#include <stdio.h>
#include <stdlib.h>
int compare(const void* a, const void* b) {
return (*(int*)a - *(int*)b);
}
int main() {
int arr[] = {5, 2, 9, 1, 5, 6};
int size = sizeof(arr) / sizeof(arr[0]);
qsort(arr, size, sizeof(int), compare); // [!code hl]
for (int i = 0; i < size; i++) {
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
```
## 结构体
### 结构体类型
结构体Structure是 C 语言中一种用户自定义的数据类型,允许将不同类型的数据组合成一个单一的类型。
```c
#include <stdio.h>
#include <string.h>
// 定义一个结构体类型
struct Person {
char name[50];
int age;
double height;
};
int main() {
// 声明一个结构体变量
struct Person person;
// 访问和修改成员并赋值
strcpy(person.name, "Alice"); // 注意:字符串赋值不能直接使用 =,需要使用 strcpy
person.age = 20;
person.height = 1.65;
printf("Name: %s, Age: %d, Height: %.2f\n", person.name, person.age, person.height);
return 0;
}
```
### 结构体指针
当我们将结构体的地址传递给指针时,如果我们想通过指针访问结构体的成员,可以使用 `->` 运算符。
通常情况下,解引用指针并访问成员的语法是 `(*p).member`,但这写起来比较麻烦(因为 `.` 的优先级高于 `*`必须加括号。C 语言提供了一个简写方式:`p->member`。
```c
#include <stdio.h>
#include <string.h>
struct Person {
char name[50];
int age;
};
int main() {
struct Person person;
struct Person* p = &person;
// 使用指针访问成员
strcpy(p->name, "Bob"); // 等价于 (*p).name
p->age = 30; // 等价于 (*p).age
printf("Name: %s, Age: %d\n", p->name, p->age);
return 0;
}
```
### 结构体的大小
结构体的大小并不总是其所有成员大小之和,因为编译器为了提高 CPU 访问内存的效率会对结构体成员进行“内存对齐”Memory Alignment
对齐规则每个成员的偏移量Offset必须是该成员大小的整数倍或者是编译器默认对齐数的较小值。结构体的总大小必须是其内部最大成员基本类型大小的整数倍。如果不足会在末尾填充字节。
让我们详细分析一下 `struct Pack` 的内存布局:
```c
struct Pack {
char c; // 1 byte
int i; // 4 bytes
};
```
- 成员 `c` (char) 占用 1 字节。放置在偏移量 0 处。
- 成员 `i` (int) 占用 4 字节。根据对齐规则,它的起始地址必须是 4 的倍数。当前的偏移量是 1`c` 之后),不是 4 的倍数。
- 编译器会在 `c` 后面插入 **3 个字节的填充Padding**,使偏移量变为 4。
- `i` 从偏移量 4 开始存储,占用第 4-7 字节。
- 此时结构体总大小为 8 字节。8 是最大成员大小 (4) 的整数倍,符合整体对齐规则。
最终的内存布局:
import StructMemoryLayout from './StructMemoryLayout.astro';
<StructMemoryLayout />
```c
#include <stdio.h>
int main() {
printf("sizeof(struct Pack) = %zu\n", sizeof(struct Pack));
// Output: sizeof(struct Pack) = 8
return 0;
}
```
因此,在计算结构体大小时,务必使用 `sizeof` 运算符,而不要简单地将成员大小相加。有时通过调整成员的定义顺序(例如将所有 `char` 放在一起),可以减少填充字节,从而减小结构体的大小。
### 自定义类型
在 C 语言中,可以使用 `typedef` 关键字来定义自定义类型。
```c title="main.c"
#include <stdio.h>
typedef int MyInt; // [!code hl]
int main() {
MyInt a = 10;
printf("a = %d\n", a);
return 0;
}
```
同样的,可以把一个复杂类型定义为自定义类型。
```c title="main.c"
#include <stdio.h>
typedef struct Point { // [!code hl]
int x; // [!code hl]
int y; // [!code hl]
} Point; // [!code hl]
int main() {
Point p; // [!code hl]
p.x = 10;
p.y = 20;
printf("p.x = %d, p.y = %d\n", p.x, p.y);
return 0;
}
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

View File

@@ -0,0 +1,280 @@
---
title: C语言快速入门
publishDate: 2022-6-10 21:00:00
description: '快速学习C语言基本语法'
tags:
- C语言
heroImage: { src: './thumbnail.webp' } # source: https://www.flickr.com/photos/isipeoria/6691167811/
language: 'zh-cn'
---
:::tip
阅读本文之前,读者应该拥有至少一门其他的编程语言基础,了解少许的 Linux 命令。
:::
## 开发环境
任意文本编辑器以及 C 编译器。本文选择 [gcc](https://gcc.gnu.org/) 在 Linux 上开发和调试。
### 安装 gcc
使用包管理器安装 gcc。某些发行版可能已经自带 gcc。
import { Tabs, TabItem } from 'astro-pure/user';
<Tabs>
<TabItem label="Linux">
```bash
# Debian / Ubuntu
sudo apt install gcc
# Redhat / CentOS / Fedora
sudo dnf install gcc
# Arch Linux / Manjaro
sudo pacman -S gcc
```
</TabItem>
<TabItem label="MacOS">
通过 [Homebrew](https://brew.sh/) 安装。
```bash
brew install gcc
```
</TabItem>
<TabItem label="Windows">
手动安装 [MinGW](https://www.mingw-w64.org/) 或通过 [winget](https://github.com/microsoft/winget-cli) 安装。
```bash
winget install -e --id Mingw.Mingw
```
</TabItem>
</Tabs>
### 文本编辑器
你可以使用任意文本编辑器,推荐 [VSCode](https://code.visualstudio.com/)。
## Hello World
创建 `main.c` 文件并编辑。
```c title="main.c"
#include <stdio.h>
int main() {
printf("Hello, World!\n");
return 0;
}
```
编译代码,运行程序。
```bash
gcc main.c -o main
./main
# Hello, World!
```
### include 预处理指令
在 C 语言中,以 `#` 开头的行是预处理器指令。`#include <stdio.h>` 告诉编译器在编译之前先包含标准输入输出库Standard Input Output的信息这样我们就可以使用 `printf` 等函数。
### main 函数
`main` 函数是程序的入口点。C 程序执行时,从这里开始。
```c
int main() {
// 代码逻辑
return 0; // 程序结束,返回 0 表示成功
}
```
## 基本类型
### 变量和类型
变量是用于存储数据的容器C 语言以以下的方式定义或声明变量。
```c
int a = 10; // 定义并初始化为 10
int b; // 声明但不初始化,仅分配一块内存
```
常见的变量类型有:
- `int`: 整数,如 10, -5现代计算机上通常为 32 位
- `float`: 单精度浮点数,例如 3.14
- `double`: 双精度浮点数,精度更高
- `char`: 单个字符,例如 'A', 'z'
`float` 和 `double` 的区别在于精度,`float` 通常为 32 位,`double` 通常为 64 位。其二进制存储方式遵循 [IEEE 754 标准](https://wikipedia.org/wiki/IEEE_754),你可以在[IEEE 754 Visualizer](https://lukaskollmer.de/ieee-754-visualizer/)直观地了解其存储原理。
```c
#include <stdio.h>
int main() {
int a = 10;
double b = 3.14159;
char c = 'A';
// %d 格式化打印整数,%f 格式化打印浮点数,%c 格式化打印字符
printf("a = %d, b = %f, c = %c\n", a, b, c);
// a = 10, b = 3.141590, c = A
return 0;
}
```
更多基本类型信息可以参考 [C 标准](https://en.cppreference.com/w/c/language/arithmetic_types.html)。
### 数组和字符串
数组是存储多个相同类型数据的容器。C 语言中没有专门的字符串类型,而是使用字符数组。数组下标从 0 开始。
```c
#include <stdio.h>
int main() {
int arr[5] = {1, 2, 3, 4, 5};
char str[] = "Hello";
printf("arr[2] = %d, str = %s\n", arr[2], str);
// arr[2] = 3, str = Hello
return 0;
}
```
C 语言规定字符串必须以空字符 `\0`数值为零的字符结尾。不像其他高级语言对字符串的封装C 语言中的字符串只是一个 `char[]` 数组,程序并不能直接得到数组的长度(之后会提到为什么),因此通过遍历字符直到 `\0` 来确定字符串在哪里结束。
在定义 `char str[] = "Hello";` 时C 语言会自动在字符串末尾添加 `\0`,因此虽然只有 5 个字符,但 `str` 的长度为 6。
```c
printf("%zu\n", sizeof(str));
// 6
```
如果字符串不以 `\0` 结尾,程序可能无法正确识别字符串的结束位置,继续连续遍历其后内存,导致未定义行为。
## 逻辑控制
### 条件分支
`if ... else` 根据条件执行不同的代码块。
```c
int num = 10;
if (num > 0) {
printf("正数\n");
} else if (num < 0) {
printf("负数\n");
} else {
printf("零\n");
}
```
:::tip
C 语言中没有原生的布尔类型,数值为 0 为逻辑假,非 0 为逻辑真。逻辑运算的结果都是整数。
:::
### 选择分支
`switch` 用于多条件判断。
```c
char grade = 'B';
switch (grade) {
case 'A':
printf("优秀\n");
break; // 跳出 switch如果不加会继续向下执行
case 'B':
printf("良好\n");
break;
default:
printf("其他\n");
}
```
### 结构
当我们知道循环次数时可以使用 `for` 循环。
```c
for (int i = 0; i < 5; i++) {
printf("i = %d\n", i);
}
```
需要重复执行直到达成某个条件,不知道具体次数时,可以用 `while` 循环。
```c
int i = 0;
while (i < 5) {
printf("i = %d\n", i);
++i;
}
```
## 函数
函数是一组一起执行一个任务的语句。每个 C 程序都至少有一个函数,即 `main` 函数。
### 定义与调用
函数由返回类型、函数名、参数列表和函数体组成。
```c
#include <stdio.h>
// 函数定义:返回 int接收两个 int 参数
int add(int x, int y) {
return x + y;
}
int main() {
int result = add(5, 3); // 函数调用
printf("result: %d\n", result);
return 0;
}
```
### 声明
不同于其他语言C 语言在调用函数时,该函数必须被声明过。尝试下面的示例,我们把 `add` 函数移动到 `main` 下方,代码就不能通过编译了。
```c
#include <stdio.h>
int main() {
int result = add(5, 3); // 错误隐式声明函数add [-Wimplicit-function-declaration] // [!code error]
printf("result: %d\n", result);
return 0;
}
int add(int x, int y) {
return x + y;
}
```
为了解决这个问题,我们可以在调用函数之前先声明函数。下面这个代码可以正常编译。
```c
#include <stdio.h>
// 函数声明:不需要函数体,以分号结尾
int add(int x, int y); // [!code ++]
int main() {
int result = add(5, 3);
printf("result: %d\n", result);
return 0;
}
// 函数定义
int add(int x, int y) {
return x + y;
}
```
import { Card } from 'astro-pure/user'
<Card as='a' href='/blog/c-pointers-and-structs' heading='下一章'
subheading='继续学习指针和结构体...'>C指针和结构体</Card>

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

View File

@@ -5,7 +5,7 @@ description: 'CSS 中最强大和最常用的布局方式之一'
tags:
- CSS
- Flex
heroImage: { src: './thumbnail.jpg' }
heroImage: { src: './thumbnail.webp' }
language: 'zh-cn'
---

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

View File

@@ -0,0 +1,72 @@
import { visit } from 'unist-util-visit'
const icons = {
note: '<g fill="none"><path d="m12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.018-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z"/><path fill="currentColor" d="M12 2c5.523 0 10 4.477 10 10s-4.477 10-10 10S2 17.523 2 12S6.477 2 12 2m0 2a8 8 0 1 0 0 16a8 8 0 0 0 0-16m-.01 6c.558 0 1.01.452 1.01 1.01v5.124A1 1 0 0 1 12.5 18h-.49A1.01 1.01 0 0 1 11 16.99V12a1 1 0 1 1 0-2zM12 7a1 1 0 1 1 0 2a1 1 0 0 1 0-2"/></g>',
tip: '<g fill="none"><path d="m12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.018-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z"/><path fill="currentColor" d="M13 20a1 1 0 1 1 0 2h-2a1 1 0 1 1 0-2zM12 2c4.41 0 8 3.543 8 7.933c0 3.006-1.522 5.196-2.78 6.494l-.284.283l-.27.252l-.252.22l-.33.27l-.328.244c-.196.138-.34.329-.466.535l-.145.251l-.141.252c-.24.412-.518.766-1.111.766h-3.786c-.593 0-.871-.354-1.11-.766l-.213-.378c-.145-.253-.305-.494-.54-.66l-.232-.171l-.199-.155l-.227-.188l-.252-.22l-.27-.252l-.285-.283C5.522 15.129 4 12.939 4 9.933C4 5.543 7.59 2 12 2m0 2C8.677 4 6 6.665 6 9.933c0 2.624 1.533 4.494 2.593 5.471l.245.218l.22.182l.27.208l.072.052c.315.222.549.531.762.854l.373.582h2.93l.373-.582c.213-.323.447-.632.762-.854l.243-.182l.206-.165l.233-.2C16.342 14.576 18 12.662 18 9.933C18 6.665 15.323 4 12 4m.293 2.293a1 1 0 0 1 1.497 1.32l-.083.094L12.414 9l1.286 1.286c.364.364.392.937.084 1.333l-.084.095l-1.993 1.993a1 1 0 0 1-1.497-1.32l.083-.094L11.586 11L10.3 9.714a1.01 1.01 0 0 1-.084-1.333l.084-.095z"/></g>',
caution: '<g fill="none"><path d="m12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.018-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z"/><path fill="currentColor" d="m13.299 3.148l8.634 14.954a1.5 1.5 0 0 1-1.299 2.25H3.366a1.5 1.5 0 0 1-1.299-2.25l8.634-14.954c.577-1 2.02-1 2.598 0M12 4.898L4.232 18.352h15.536zM12 15a1 1 0 1 1 0 2a1 1 0 0 1 0-2m0-7a1 1 0 0 1 1 1v4a1 1 0 1 1-2 0V9a1 1 0 0 1 1-1"/></g>',
danger: '<g fill="none"><path d="m12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.018-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z"/><path fill="currentColor" d="M15.314 2a2 2 0 0 1 1.414.586l4.686 4.686A2 2 0 0 1 22 8.686v6.628a2 2 0 0 1-.586 1.414l-4.686 4.686a2 2 0 0 1-1.414.586H8.686a2 2 0 0 1-1.414-.586l-4.686-4.686A2 2 0 0 1 2 15.314V8.686a2 2 0 0 1 .586-1.414l4.686-4.686A2 2 0 0 1 8.686 2zm0 2H8.686L4 8.686v6.628L8.686 20h6.628L20 15.314V8.686zM12 15a1 1 0 1 1 0 2a1 1 0 0 1 0-2m0-9a1 1 0 0 1 1 1v6a1 1 0 1 1-2 0V7a1 1 0 0 1 1-1"/></g>'
}
export function remarkAside() {
return (tree: any) => {
visit(tree, (node: any) => {
if (node.type !== 'containerDirective') return
const type = node.name
if (!icons[type as keyof typeof icons]) return
const title = node.attributes?.title || type.toUpperCase()
const children = node.children
const titleNode = {
type: 'paragraph',
data: {
hName: 'p',
hProperties: {
class: 'not-prose flex items-center gap-x-2 font-medium text-primary',
'aria-hidden': 'true'
}
},
children: [
{
type: 'html',
value: `<svg class="icon" width="22" height="22" viewBox="0 0 24 24" fill="currentColor">${icons[type as keyof typeof icons]}</svg>`
},
{ type: 'text', value: title }
]
}
const contentNode = {
type: 'containerDirective',
name: 'div',
data: {
hName: 'div',
hProperties: { class: 'aside-content mt-2' }
},
children: children
}
const containerNode = {
type: 'containerDirective',
name: 'div',
data: {
hName: 'div',
hProperties: {
class: `aside-container border-l-8 border-primary px-4 py-3 bg-primary aside-${type}`
}
},
children: [titleNode, contentNode]
}
node.name = 'aside'
node.data = {
hName: 'aside',
hProperties: {
class: 'aside my-3 overflow-hidden rounded-xl border',
'aria-label': title
}
}
node.children = [containerNode]
})
}
}

View File

@@ -1,166 +0,0 @@
// https://github.com/shikijs/shiki/tree/main/packages/transformers
import type { Element, Text } from 'hast'
import type { ShikiTransformer, ShikiTransformerContext } from 'shiki'
interface TransformerNotationMapOptions {
classMap?: Record<string, string | string[]>
/**
* Class added to the <pre> element when the current code has diff
*/
classActivePre?: string
}
function createCommentNotationTransformer(
name: string,
regex: RegExp,
onMatch: (
this: ShikiTransformerContext,
match: string[],
line: Element,
commentNode: Element,
lines: Element[],
index: number
) => boolean,
removeEmptyLines = false
): ShikiTransformer {
return {
name,
code(code) {
const lines = code.children.filter((i) => i.type === 'element') as Element[]
const linesToRemove: (Element | Text)[] = []
lines.forEach((line, idx) => {
let nodeToRemove: Element | undefined
for (const child of line.children) {
if (child.type !== 'element') continue
const text = child.children[0]
if (text.type !== 'text') continue
let replaced = false
text.value = text.value.replace(regex, (...match) => {
if (onMatch.call(this, match, line, child, lines, idx)) {
replaced = true
return ''
}
return match[0]
})
if (replaced && !text.value.trim()) nodeToRemove = child
}
if (nodeToRemove) {
line.children.splice(line.children.indexOf(nodeToRemove), 1)
// Remove if empty
if (line.children.length === 0) {
linesToRemove.push(line)
if (removeEmptyLines) {
const next = code.children[code.children.indexOf(line) + 1]
if (next && next.type === 'text' && next.value === '\n') linesToRemove.push(next)
}
}
}
})
for (const line of linesToRemove) code.children.splice(code.children.indexOf(line), 1)
}
}
}
function escapeRegExp(str: string): string {
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
}
function transformerNotationMap(
options: TransformerNotationMapOptions = {},
name = '@shikijs/transformers:notation-map'
): ShikiTransformer {
const { classMap = {}, classActivePre = undefined } = options
return createCommentNotationTransformer(
name,
new RegExp(
`\\s*(?://|/\\*|<!--|#|--|%{1,2}|;{1,2}|"|')\\s+\\[!code (${Object.keys(classMap).map(escapeRegExp).join('|')})(:\\d+)?\\]\\s*(?:\\*/|-->)?\\s*$`
),
function ([, match, range = ':1'], _line, _comment, lines, index) {
const lineNum = Number.parseInt(range.slice(1), 10)
lines.slice(index, index + lineNum).forEach((line) => {
this.addClassToHast(line, classMap[match])
})
if (classActivePre) this.addClassToHast(this.pre, classActivePre)
return true
}
)
}
// === Transformers ===
// Add a diff notation to the code block
export interface TransformerNotationDiffOptions {
/**
* Class for added lines
*/
classLineAdd?: string
/**
* Class for removed lines
*/
classLineRemove?: string
/**
* Class added to the <pre> element when the current code has diff
*/
classActivePre?: string
}
/**
* Use `[!code ++]` and `[!code --]` to mark added and removed lines.
*/
export function transformerNotationDiff(
options: TransformerNotationDiffOptions = {}
): ShikiTransformer {
const {
classLineAdd = 'diff add',
classLineRemove = 'diff remove',
classActivePre = 'has-diff'
} = options
return transformerNotationMap(
{
classMap: {
'++': classLineAdd,
'--': classLineRemove
},
classActivePre
},
'@shikijs/transformers:notation-diff'
)
}
// Add a highlight notation to the code block
// https://github.com/shikijs/shiki/blob/main/packages/transformers/src/transformers/notation-highlight.ts
export interface TransformerNotationHighlightOptions {
/**
* Class for highlighted lines
*/
classActiveLine?: string
/**
* Class added to the root element when the code has highlighted lines
*/
classActivePre?: string
}
/**
* Allow using `[!code highlight]` notation in code to mark highlighted lines.
*/
export function transformerNotationHighlight(
options: TransformerNotationHighlightOptions = {}
): ShikiTransformer {
const { classActiveLine = 'highlighted', classActivePre = 'has-highlighted' } = options
return transformerNotationMap(
{
classMap: {
highlight: classActiveLine,
hl: classActiveLine
},
classActivePre
},
'@shikijs/transformers:notation-highlight'
)
}

View File

@@ -3,8 +3,10 @@ import type { ShikiTransformer } from 'shiki'
export {
transformerNotationDiff,
transformerNotationHighlight
} from './shiki-official-transformers'
transformerNotationHighlight,
transformerNotationFocus,
transformerNotationErrorLevel
} from '@shikijs/transformers'
function parseMetaString(str = '') {
return Object.fromEntries(