mirror of
https://github.com/awfufu/awfufu.com.git
synced 2026-03-01 04:29:42 +08:00
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:
@@ -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(),
|
||||
|
||||
30
bun.lock
30
bun.lock
@@ -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=="],
|
||||
|
||||
@@ -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/*"
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
79
src/content/blog/c-pointers-and-structs/PointerDiagram.astro
Normal file
79
src/content/blog/c-pointers-and-structs/PointerDiagram.astro
Normal 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>
|
||||
129
src/content/blog/c-pointers-and-structs/StructMemoryLayout.astro
Normal file
129
src/content/blog/c-pointers-and-structs/StructMemoryLayout.astro
Normal 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>
|
||||
502
src/content/blog/c-pointers-and-structs/index.mdx
Normal file
502
src/content/blog/c-pointers-and-structs/index.mdx
Normal 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;
|
||||
}
|
||||
```
|
||||
BIN
src/content/blog/c-pointers-and-structs/thumbnail.webp
Normal file
BIN
src/content/blog/c-pointers-and-structs/thumbnail.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 63 KiB |
280
src/content/blog/c-quick-start/index.mdx
Normal file
280
src/content/blog/c-quick-start/index.mdx
Normal 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>
|
||||
BIN
src/content/blog/c-quick-start/thumbnail.webp
Normal file
BIN
src/content/blog/c-quick-start/thumbnail.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 37 KiB |
@@ -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 |
BIN
src/content/blog/css-flex-layout/thumbnail.webp
Normal file
BIN
src/content/blog/css-flex-layout/thumbnail.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 69 KiB |
72
src/plugins/remark-aside.ts
Normal file
72
src/plugins/remark-aside.ts
Normal 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]
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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'
|
||||
)
|
||||
}
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user