<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.2.2">Jekyll</generator><link href="https://savemejeff.github.io//feed.xml" rel="self" type="application/atom+xml" /><link href="https://savemejeff.github.io//" rel="alternate" type="text/html" /><updated>2023-12-10T14:45:50+08:00</updated><id>https://savemejeff.github.io//feed.xml</id><title type="html">Jiang Jingwei’s Blog</title><subtitle>三本计科，啥也不会</subtitle><entry><title type="html">从零构建一个Redis</title><link href="https://savemejeff.github.io//jiang/jingwei/2023/12/07/build-my-own-redis-from-scratch.html" rel="alternate" type="text/html" title="从零构建一个Redis" /><published>2023-12-07T11:15:00+08:00</published><updated>2023-12-07T11:15:00+08:00</updated><id>https://savemejeff.github.io//jiang/jingwei/2023/12/07/build-my-own-redis-from-scratch</id><content type="html" xml:base="https://savemejeff.github.io//jiang/jingwei/2023/12/07/build-my-own-redis-from-scratch.html"><![CDATA[<h2 id="1hello-serverclient">1.Hello Server/Client</h2>

<p>Redis是一个server/client架构的系统，多个clients连接到一个server，server通过TCP连接接收请求并发送响应。</p>

<p>下面是一些system call的介绍：</p>

<ol>
  <li>
    <p><code class="language-plaintext highlighter-rouge">socket()</code>: 创建一个socket并返回对应的文件描述符<code class="language-plaintext highlighter-rouge">fd</code>，文件描述符是一个integer，指向Linux内核中的资源，比如TCP连接，磁盘上的文件，正在监听的端口等。</p>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">bind()</code>: 将一个地址与socket关联起来。</p>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">listen()</code>: 使socket能接受连接。</p>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">accept()</code>: 当一个client尝试连接到正在监听的地址，<code class="language-plaintext highlighter-rouge">accept()</code>会返回一个<code class="language-plaintext highlighter-rouge">fd</code>，代表connection socket。</p>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">read()</code>, <code class="language-plaintext highlighter-rouge">write()</code>: 从TCP connection中读取或向TCP connection中写入数据。</p>
  </li>
</ol>

<p>下面的代码是一个基本的server/client架构的系统的实现。server和client会分别向对方发送一个”hello”，并打印出来自对方的数据。</p>

<p>一些需要注意的细节：我们给socket绑定地址和端口时，调用了<code class="language-plaintext highlighter-rouge">ntohs()</code>和<code class="language-plaintext highlighter-rouge">ntohl()</code>，这是因为TCP协议规定数据应该使用大端序（big-endian）来传输。<code class="language-plaintext highlighter-rouge">ntohs()</code>和<code class="language-plaintext highlighter-rouge">ntohl()</code>的作用是将网络字节序（big-endian）转换为主机字节序（在英特尔平台上是little-endian）。</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// server.cpp</span>

<span class="cp">#include</span> <span class="cpf">&lt;stdint.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stdlib.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;string.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;errno.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;unistd.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;arpa/inet.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;sys/socket.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;netinet/ip.h&gt;</span><span class="cp">
</span>

<span class="k">static</span> <span class="kt">void</span> <span class="nf">msg</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">msg</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">"%s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">msg</span><span class="p">);</span>
<span class="p">}</span>

<span class="k">static</span> <span class="kt">void</span> <span class="nf">die</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">msg</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">int</span> <span class="n">err</span> <span class="o">=</span> <span class="n">errno</span><span class="p">;</span>
    <span class="n">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">"[%d] %s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">err</span><span class="p">,</span> <span class="n">msg</span><span class="p">);</span>
    <span class="n">abort</span><span class="p">();</span>
<span class="p">}</span>

<span class="k">static</span> <span class="kt">void</span> <span class="nf">do_something</span><span class="p">(</span><span class="kt">int</span> <span class="n">connfd</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">char</span> <span class="n">rbuf</span><span class="p">[</span><span class="mi">64</span><span class="p">]</span> <span class="o">=</span> <span class="p">{};</span>
    <span class="kt">ssize_t</span> <span class="n">n</span> <span class="o">=</span> <span class="n">read</span><span class="p">(</span><span class="n">connfd</span><span class="p">,</span> <span class="n">rbuf</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">rbuf</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">n</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">msg</span><span class="p">(</span><span class="s">"read() error"</span><span class="p">);</span>
        <span class="k">return</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"client says: %s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">rbuf</span><span class="p">);</span>

    <span class="kt">char</span> <span class="n">wbuf</span><span class="p">[]</span> <span class="o">=</span> <span class="s">"world"</span><span class="p">;</span>
    <span class="n">write</span><span class="p">(</span><span class="n">connfd</span><span class="p">,</span> <span class="n">wbuf</span><span class="p">,</span> <span class="n">strlen</span><span class="p">(</span><span class="n">wbuf</span><span class="p">));</span>
<span class="p">}</span>

<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="kt">int</span> <span class="n">fd</span> <span class="o">=</span> <span class="n">socket</span><span class="p">(</span><span class="n">AF_INET</span><span class="p">,</span> <span class="n">SOCK_STREAM</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">fd</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">die</span><span class="p">(</span><span class="s">"socket()"</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="c1">// this is needed for most server applications</span>
    <span class="kt">int</span> <span class="n">val</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
    <span class="n">setsockopt</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="n">SOL_SOCKET</span><span class="p">,</span> <span class="n">SO_REUSEADDR</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">val</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">val</span><span class="p">));</span>

    <span class="c1">// bind</span>
    <span class="k">struct</span> <span class="nc">sockaddr_in</span> <span class="n">addr</span> <span class="o">=</span> <span class="p">{};</span>
    <span class="n">addr</span><span class="p">.</span><span class="n">sin_family</span> <span class="o">=</span> <span class="n">AF_INET</span><span class="p">;</span>
    <span class="n">addr</span><span class="p">.</span><span class="n">sin_port</span> <span class="o">=</span> <span class="n">ntohs</span><span class="p">(</span><span class="mi">1234</span><span class="p">);</span>
    <span class="n">addr</span><span class="p">.</span><span class="n">sin_addr</span><span class="p">.</span><span class="n">s_addr</span> <span class="o">=</span> <span class="n">ntohl</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>    <span class="c1">// wildcard address 0.0.0.0</span>
    <span class="kt">int</span> <span class="n">rv</span> <span class="o">=</span> <span class="n">bind</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="p">(</span><span class="k">const</span> <span class="n">sockaddr</span> <span class="o">*</span><span class="p">)</span><span class="o">&amp;</span><span class="n">addr</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">addr</span><span class="p">));</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">rv</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">die</span><span class="p">(</span><span class="s">"bind()"</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="c1">// listen</span>
    <span class="n">rv</span> <span class="o">=</span> <span class="n">listen</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="n">SOMAXCONN</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">rv</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">die</span><span class="p">(</span><span class="s">"listen()"</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="k">while</span> <span class="p">(</span><span class="nb">true</span><span class="p">)</span> <span class="p">{</span>
        <span class="c1">// accept</span>
        <span class="k">struct</span> <span class="nc">sockaddr_in</span> <span class="n">client_addr</span> <span class="o">=</span> <span class="p">{};</span>
        <span class="n">socklen_t</span> <span class="n">socklen</span> <span class="o">=</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">client_addr</span><span class="p">);</span>
        <span class="kt">int</span> <span class="n">connfd</span> <span class="o">=</span> <span class="n">accept</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="p">(</span><span class="k">struct</span> <span class="nc">sockaddr</span> <span class="o">*</span><span class="p">)</span><span class="o">&amp;</span><span class="n">client_addr</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">socklen</span><span class="p">);</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">connfd</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">continue</span><span class="p">;</span>   <span class="c1">// error</span>
        <span class="p">}</span>

        <span class="n">do_something</span><span class="p">(</span><span class="n">connfd</span><span class="p">);</span>
        <span class="n">close</span><span class="p">(</span><span class="n">connfd</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// client.cpp</span>

<span class="cp">#include</span> <span class="cpf">&lt;stdint.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stdlib.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;string.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;errno.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;unistd.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;arpa/inet.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;sys/socket.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;netinet/ip.h&gt;</span><span class="cp">
</span>

<span class="k">static</span> <span class="kt">void</span> <span class="nf">die</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">msg</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">int</span> <span class="n">err</span> <span class="o">=</span> <span class="n">errno</span><span class="p">;</span>
    <span class="n">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">"[%d] %s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">err</span><span class="p">,</span> <span class="n">msg</span><span class="p">);</span>
    <span class="n">abort</span><span class="p">();</span>
<span class="p">}</span>

<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="kt">int</span> <span class="n">fd</span> <span class="o">=</span> <span class="n">socket</span><span class="p">(</span><span class="n">AF_INET</span><span class="p">,</span> <span class="n">SOCK_STREAM</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">fd</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">die</span><span class="p">(</span><span class="s">"socket()"</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="k">struct</span> <span class="nc">sockaddr_in</span> <span class="n">addr</span> <span class="o">=</span> <span class="p">{};</span>
    <span class="n">addr</span><span class="p">.</span><span class="n">sin_family</span> <span class="o">=</span> <span class="n">AF_INET</span><span class="p">;</span>
    <span class="n">addr</span><span class="p">.</span><span class="n">sin_port</span> <span class="o">=</span> <span class="n">ntohs</span><span class="p">(</span><span class="mi">1234</span><span class="p">);</span>
    <span class="n">addr</span><span class="p">.</span><span class="n">sin_addr</span><span class="p">.</span><span class="n">s_addr</span> <span class="o">=</span> <span class="n">ntohl</span><span class="p">(</span><span class="n">INADDR_LOOPBACK</span><span class="p">);</span>  <span class="c1">// 127.0.0.1</span>
    <span class="kt">int</span> <span class="n">rv</span> <span class="o">=</span> <span class="n">connect</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="p">(</span><span class="k">const</span> <span class="k">struct</span> <span class="nc">sockaddr</span> <span class="o">*</span><span class="p">)</span><span class="o">&amp;</span><span class="n">addr</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">addr</span><span class="p">));</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">rv</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">die</span><span class="p">(</span><span class="s">"connect"</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="kt">char</span> <span class="n">msg</span><span class="p">[]</span> <span class="o">=</span> <span class="s">"hello"</span><span class="p">;</span>
    <span class="n">write</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="n">msg</span><span class="p">,</span> <span class="n">strlen</span><span class="p">(</span><span class="n">msg</span><span class="p">));</span>

    <span class="kt">char</span> <span class="n">rbuf</span><span class="p">[</span><span class="mi">64</span><span class="p">]</span> <span class="o">=</span> <span class="p">{};</span>
    <span class="kt">ssize_t</span> <span class="n">n</span> <span class="o">=</span> <span class="n">read</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="n">rbuf</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">rbuf</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">n</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">die</span><span class="p">(</span><span class="s">"read"</span><span class="p">);</span>
    <span class="p">}</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"server says: %s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">rbuf</span><span class="p">);</span>
    <span class="n">close</span><span class="p">(</span><span class="n">fd</span><span class="p">);</span>
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>]]></content><author><name></name></author><category term="Jiang" /><category term="Jingwei" /><summary type="html"><![CDATA[1.Hello Server/Client Redis是一个server/client架构的系统，多个clients连接到一个server，server通过TCP连接接收请求并发送响应。 下面是一些system call的介绍： socket(): 创建一个socket并返回对应的文件描述符fd，文件描述符是一个integer，指向Linux内核中的资源，比如TCP连接，磁盘上的文件，正在监听的端口等。 bind(): 将一个地址与socket关联起来。 listen(): 使socket能接受连接。 accept(): 当一个client尝试连接到正在监听的地址，accept()会返回一个fd，代表connection socket。 read(), write(): 从TCP connection中读取或向TCP connection中写入数据。 下面的代码是一个基本的server/client架构的系统的实现。server和client会分别向对方发送一个”hello”，并打印出来自对方的数据。 一些需要注意的细节：我们给socket绑定地址和端口时，调用了ntohs()和ntohl()，这是因为TCP协议规定数据应该使用大端序（big-endian）来传输。ntohs()和ntohl()的作用是将网络字节序（big-endian）转换为主机字节序（在英特尔平台上是little-endian）。 // server.cpp #include &lt;stdint.h&gt; #include &lt;stdlib.h&gt; #include &lt;string.h&gt; #include &lt;stdio.h&gt; #include &lt;errno.h&gt; #include &lt;unistd.h&gt; #include &lt;arpa/inet.h&gt; #include &lt;sys/socket.h&gt; #include &lt;netinet/ip.h&gt; static void msg(const char *msg) { fprintf(stderr, "%s\n", msg); } static void die(const char *msg) { int err = errno; fprintf(stderr, "[%d] %s\n", err, msg); abort(); } static void do_something(int connfd) { char rbuf[64] = {}; ssize_t n = read(connfd, rbuf, sizeof(rbuf) - 1); if (n &lt; 0) { msg("read() error"); return; } printf("client says: %s\n", rbuf); char wbuf[] = "world"; write(connfd, wbuf, strlen(wbuf)); } int main() { int fd = socket(AF_INET, SOCK_STREAM, 0); if (fd &lt; 0) { die("socket()"); } // this is needed for most server applications int val = 1; setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &amp;val, sizeof(val)); // bind struct sockaddr_in addr = {}; addr.sin_family = AF_INET; addr.sin_port = ntohs(1234); addr.sin_addr.s_addr = ntohl(0); // wildcard address 0.0.0.0 int rv = bind(fd, (const sockaddr *)&amp;addr, sizeof(addr)); if (rv) { die("bind()"); } // listen rv = listen(fd, SOMAXCONN); if (rv) { die("listen()"); } while (true) { // accept struct sockaddr_in client_addr = {}; socklen_t socklen = sizeof(client_addr); int connfd = accept(fd, (struct sockaddr *)&amp;client_addr, &amp;socklen); if (connfd &lt; 0) { continue; // error } do_something(connfd); close(connfd); } return 0; } // client.cpp #include &lt;stdint.h&gt; #include &lt;stdlib.h&gt; #include &lt;string.h&gt; #include &lt;stdio.h&gt; #include &lt;errno.h&gt; #include &lt;unistd.h&gt; #include &lt;arpa/inet.h&gt; #include &lt;sys/socket.h&gt; #include &lt;netinet/ip.h&gt; static void die(const char *msg) { int err = errno; fprintf(stderr, "[%d] %s\n", err, msg); abort(); } int main() { int fd = socket(AF_INET, SOCK_STREAM, 0); if (fd &lt; 0) { die("socket()"); } struct sockaddr_in addr = {}; addr.sin_family = AF_INET; addr.sin_port = ntohs(1234); addr.sin_addr.s_addr = ntohl(INADDR_LOOPBACK); // 127.0.0.1 int rv = connect(fd, (const struct sockaddr *)&amp;addr, sizeof(addr)); if (rv) { die("connect"); } char msg[] = "hello"; write(fd, msg, strlen(msg)); char rbuf[64] = {}; ssize_t n = read(fd, rbuf, sizeof(rbuf) - 1); if (n &lt; 0) { die("read"); } printf("server says: %s\n", rbuf); close(fd); return 0; }]]></summary></entry><entry><title type="html">C#中的ref关键字</title><link href="https://savemejeff.github.io//jiang/jingwei/2022/05/30/ref-keyword-in-csharp.html" rel="alternate" type="text/html" title="C#中的ref关键字" /><published>2022-05-30T12:19:00+08:00</published><updated>2022-05-30T12:19:00+08:00</updated><id>https://savemejeff.github.io//jiang/jingwei/2022/05/30/ref-keyword-in-csharp</id><content type="html" xml:base="https://savemejeff.github.io//jiang/jingwei/2022/05/30/ref-keyword-in-csharp.html"><![CDATA[<p>首先看微软的文档中是怎么说的。</p>

<blockquote>
  <p><code class="language-plaintext highlighter-rouge">ref</code> 关键字指示按引用传递值。 它用在四种不同的上下文中：</p>
</blockquote>

<p>那么什么叫按引用传递值呢？</p>

<p>在C#中，所有类型的变量（无论值类型还是引用类型），都是按值传递的。让我们先看看按值传递是怎么工作的。</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="n">a</span> <span class="p">=</span> <span class="m">10</span><span class="p">;</span>
<span class="kt">int</span> <span class="n">b</span> <span class="p">=</span> <span class="n">a</span><span class="p">;</span>
<span class="n">b</span> <span class="p">=</span> <span class="m">20</span><span class="p">;</span>
<span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">$"a: </span><span class="p">{</span><span class="n">a</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>
<span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">$"b: </span><span class="p">{</span><span class="n">b</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>
<span class="c1">// Output: </span>
<span class="c1">// a: 10</span>
<span class="c1">// b: 20</span>
</code></pre></div></div>
<p>值传递时，改变b的值并不会影响a。</p>

<p>下面看看按引用传递是如何工作的。</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="n">a</span> <span class="p">=</span> <span class="m">10</span><span class="p">;</span>
<span class="k">ref</span> <span class="kt">int</span> <span class="n">b</span> <span class="p">=</span> <span class="k">ref</span> <span class="n">a</span><span class="p">;</span>
<span class="n">b</span> <span class="p">=</span> <span class="m">20</span><span class="p">;</span>
<span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">$"a: </span><span class="p">{</span><span class="n">a</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>
<span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">$"b: </span><span class="p">{</span><span class="n">b</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>
<span class="c1">// Output: </span>
<span class="c1">// a: 20</span>
<span class="c1">// b: 20</span>
</code></pre></div></div>
<p>按引用传递时，对b的赋值会影响变量a，因为b是一个指向变量a的引用。</p>

<p>学过C语言的同学是不是有种似曾相识的感觉？</p>

<pre><code class="language-C">int a = 10;
int* b = &amp;a;
*b = 20;
printf("a: %d", a);
printf("b: %d", *b);
// Output:
// a: 20
// b: 20
</code></pre>
<p>虽然不是相同的概念，但是ref的功能其实与C语言中的指针十分相似。</p>

<p>当然，使用<code class="language-plaintext highlighter-rouge">ref</code>关键字去引用在同一个作用域中的变量是没有任何意义的。</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="n">a</span> <span class="p">=</span> <span class="m">10</span><span class="p">;</span>
<span class="c1">// ref int b = ref a;</span>
<span class="c1">// b = 20; </span>
<span class="n">a</span> <span class="p">=</span> <span class="m">20</span><span class="p">;</span> <span class="c1">// 你应该这样写。</span>
</code></pre></div></div>

<p>正确的使用方式如下所示：</p>

<blockquote>
  <p>在方法签名和方法调用中，按引用将参数传递给方法。</p>
</blockquote>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">void</span> <span class="nf">Method</span><span class="p">(</span><span class="k">ref</span> <span class="kt">int</span> <span class="n">num</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">n</span> <span class="p">+=</span> <span class="m">1</span><span class="p">;</span>
<span class="p">}</span>

<span class="kt">int</span> <span class="n">n</span> <span class="p">=</span> <span class="m">10</span><span class="p">;</span>
<span class="nf">Method</span><span class="p">(</span><span class="k">ref</span> <span class="n">n</span><span class="p">);</span>
<span class="c1">// 执行Method方法后，能够改变实参n的值。</span>
</code></pre></div></div>

<blockquote>
  <p>在方法签名中，按引用将值返回给调用方。</p>
</blockquote>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">ref</span> <span class="n">SomeStruct</span> <span class="nf">GetStruct</span><span class="p">()</span>
<span class="p">{</span>
    <span class="n">SomeStruct</span> <span class="n">s</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">SomeStruct</span><span class="p">();</span>
    <span class="k">return</span> <span class="k">ref</span> <span class="n">s</span><span class="p">;</span>
<span class="p">}</span>

<span class="k">ref</span> <span class="kt">var</span> <span class="k">struct</span> <span class="err">=</span> <span class="nc">ref</span> <span class="nf">GetStruct</span><span class="p">();</span>
<span class="c1">// 按引用将值返回一个复杂的结构体，能够有效减小开销。</span>
</code></pre></div></div>
<p>下面这两个用法还没看明白，等看明白了再补一个。</p>

<blockquote>
  <p>在成员正文中，指示引用返回值是否作为调用方欲修改的引用被存储在本地。 或指示局部变量按引用访问另一个值。</p>
</blockquote>

<blockquote>
  <p>在 <code class="language-plaintext highlighter-rouge">struct</code> 声明中，声明 <code class="language-plaintext highlighter-rouge">ref struct</code> 或 <code class="language-plaintext highlighter-rouge">readonly ref struct</code>。</p>
</blockquote>

<p>另外还有两个关键字 <code class="language-plaintext highlighter-rouge">in</code> 和 <code class="language-plaintext highlighter-rouge">out</code> ，与 <code class="language-plaintext highlighter-rouge">ref</code> 比较相似。</p>

<p>不同之处在于 <code class="language-plaintext highlighter-rouge">in</code> 只能向被调用的方法传递引用，而在被调用的方法中，被<code class="language-plaintext highlighter-rouge">in</code>修饰的参数是只读的。而 <code class="language-plaintext highlighter-rouge">out</code> 只能从被调用的方法向方法的调用方传递引用。</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="n">n</span> <span class="p">=</span> <span class="m">10</span><span class="p">;</span>
<span class="k">void</span> <span class="nf">InMethod</span><span class="p">(</span><span class="k">in</span> <span class="kt">int</span> <span class="n">num</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">num</span> <span class="p">=</span> <span class="m">1</span><span class="p">;</span>
<span class="p">}</span>
<span class="nf">InMethod</span><span class="p">(</span><span class="k">in</span> <span class="n">n</span><span class="p">);</span>
<span class="c1">// error CS8331: Cannot assign to variable 'in int' because it is a readonly variable</span>
<span class="c1">// in 参数被视为只读。</span>

<span class="k">void</span> <span class="nf">OutMethod</span><span class="p">(</span><span class="k">out</span> <span class="kt">int</span> <span class="n">num</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">num</span><span class="p">++;</span>
<span class="p">}</span>
<span class="nf">OutMethod</span><span class="p">(</span><span class="k">out</span> <span class="n">n</span><span class="p">);</span>
<span class="c1">// error CS0269: Use of unassigned out parameter 'num'</span>
<span class="c1">// out 参数被视为未初始化的变量。</span>
</code></pre></div></div>]]></content><author><name></name></author><category term="Jiang" /><category term="Jingwei" /><summary type="html"><![CDATA[首先看微软的文档中是怎么说的。 ref 关键字指示按引用传递值。 它用在四种不同的上下文中： 那么什么叫按引用传递值呢？ 在C#中，所有类型的变量（无论值类型还是引用类型），都是按值传递的。让我们先看看按值传递是怎么工作的。 int a = 10; int b = a; b = 20; Console.WriteLine($"a: {a}"); Console.WriteLine($"b: {b}"); // Output: // a: 10 // b: 20 值传递时，改变b的值并不会影响a。 下面看看按引用传递是如何工作的。 int a = 10; ref int b = ref a; b = 20; Console.WriteLine($"a: {a}"); Console.WriteLine($"b: {b}"); // Output: // a: 20 // b: 20 按引用传递时，对b的赋值会影响变量a，因为b是一个指向变量a的引用。 学过C语言的同学是不是有种似曾相识的感觉？ int a = 10; int* b = &amp;a; *b = 20; printf("a: %d", a); printf("b: %d", *b); // Output: // a: 20 // b: 20 虽然不是相同的概念，但是ref的功能其实与C语言中的指针十分相似。 当然，使用ref关键字去引用在同一个作用域中的变量是没有任何意义的。 int a = 10; // ref int b = ref a; // b = 20; a = 20; // 你应该这样写。 正确的使用方式如下所示： 在方法签名和方法调用中，按引用将参数传递给方法。 void Method(ref int num) { n += 1; } int n = 10; Method(ref n); // 执行Method方法后，能够改变实参n的值。 在方法签名中，按引用将值返回给调用方。 ref SomeStruct GetStruct() { SomeStruct s = new SomeStruct(); return ref s; } ref var struct = ref GetStruct(); // 按引用将值返回一个复杂的结构体，能够有效减小开销。 下面这两个用法还没看明白，等看明白了再补一个。 在成员正文中，指示引用返回值是否作为调用方欲修改的引用被存储在本地。 或指示局部变量按引用访问另一个值。 在 struct 声明中，声明 ref struct 或 readonly ref struct。 另外还有两个关键字 in 和 out ，与 ref 比较相似。 不同之处在于 in 只能向被调用的方法传递引用，而在被调用的方法中，被in修饰的参数是只读的。而 out 只能从被调用的方法向方法的调用方传递引用。 int n = 10; void InMethod(in int num) { num = 1; } InMethod(in n); // error CS8331: Cannot assign to variable 'in int' because it is a readonly variable // in 参数被视为只读。 void OutMethod(out int num) { num++; } OutMethod(out n); // error CS0269: Use of unassigned out parameter 'num' // out 参数被视为未初始化的变量。]]></summary></entry><entry><title type="html">First github page blog</title><link href="https://savemejeff.github.io//jiang/jingwei/2022/05/29/first-github-page-blog.html" rel="alternate" type="text/html" title="First github page blog" /><published>2022-05-29T19:21:00+08:00</published><updated>2022-05-29T19:21:00+08:00</updated><id>https://savemejeff.github.io//jiang/jingwei/2022/05/29/first-github-page-blog</id><content type="html" xml:base="https://savemejeff.github.io//jiang/jingwei/2022/05/29/first-github-page-blog.html"><![CDATA[<p>花了一个多小时，总算把这个博客搭起来了。准备用这个博客用来记录写代码时遇到的坑，以及一些杂七杂八的东西。</p>

<p>这一篇顺便记录一下搭博客的时候遇到的坑。</p>

<h4 id="搭建博客的整体思路">搭建博客的整体思路</h4>

<p>整体的思路来自油管上的这个<a href="https://www.youtube.com/watch?v=LIFvgrRxdt4">视频</a>。</p>
<ul>
  <li>创建两个repo。一个保存博客的源码，一个保存由工具生成的静态网站代码。</li>
  <li>通过<code class="language-plaintext highlighter-rouge">git</code>的<code class="language-plaintext highlighter-rouge">submodule</code>功能，在一个文件夹下，同时管理两个repo。</li>
</ul>

<h4 id="github-codespaces">Github CodeSpaces</h4>

<p>很久之前注册了<code class="language-plaintext highlighter-rouge">Code Spaces</code>的测试计划。最近收到了进入测试计划的邮件，就想着用<code class="language-plaintext highlighter-rouge">Code Spcaes</code>去搭建和更新一个博客，同时也给自己留了个坑。</p>

<h4 id="jekyll">Jekyll</h4>

<p><code class="language-plaintext highlighter-rouge">Jekyll</code>（不知道怎么念）是github默认的Pages模板。执行<code class="language-plaintext highlighter-rouge">jekyll b</code>生成静态的网站。</p>

<h4 id="坑">坑</h4>

<ul>
  <li>不知什么原因，在<code class="language-plaintext highlighter-rouge">Code Spaces</code>中，管理<code class="language-plaintext highlighter-rouge">submodule</code>的时候push总是失败，提示没有权限。</li>
  <li>Github Pages在选择theme之前没有生效。</li>
</ul>

<h4 id="摸爬滚打">摸爬滚打</h4>

<ul>
  <li>尝试配置<code class="language-plaintext highlighter-rouge">user.name</code>和<code class="language-plaintext highlighter-rouge">user.email</code></li>
  <li>尝试配置<code class="language-plaintext highlighter-rouge">ssh key</code></li>
  <li>尝试配置remote url为<code class="language-plaintext highlighter-rouge">user@github.com/reponame.git</code></li>
</ul>

<h4 id="最终的解决方案">最终的解决方案</h4>

<p>只找到了一个能临时解决问题的方案。在<code class="language-plaintext highlighter-rouge">github - settings - developer settings - personal access tokens</code>中生成一个新的token，然后修改remote url为<code class="language-plaintext highlighter-rouge">token@github.com/reponame.git</code></p>]]></content><author><name></name></author><category term="Jiang" /><category term="Jingwei" /><summary type="html"><![CDATA[花了一个多小时，总算把这个博客搭起来了。准备用这个博客用来记录写代码时遇到的坑，以及一些杂七杂八的东西。 这一篇顺便记录一下搭博客的时候遇到的坑。 搭建博客的整体思路 整体的思路来自油管上的这个视频。 创建两个repo。一个保存博客的源码，一个保存由工具生成的静态网站代码。 通过git的submodule功能，在一个文件夹下，同时管理两个repo。 Github CodeSpaces 很久之前注册了Code Spaces的测试计划。最近收到了进入测试计划的邮件，就想着用Code Spcaes去搭建和更新一个博客，同时也给自己留了个坑。 Jekyll Jekyll（不知道怎么念）是github默认的Pages模板。执行jekyll b生成静态的网站。 坑 不知什么原因，在Code Spaces中，管理submodule的时候push总是失败，提示没有权限。 Github Pages在选择theme之前没有生效。 摸爬滚打 尝试配置user.name和user.email 尝试配置ssh key 尝试配置remote url为user@github.com/reponame.git 最终的解决方案 只找到了一个能临时解决问题的方案。在github - settings - developer settings - personal access tokens中生成一个新的token，然后修改remote url为token@github.com/reponame.git]]></summary></entry><entry><title type="html">Welcome to Jekyll!</title><link href="https://savemejeff.github.io//jekyll/update/2022/05/29/welcome-to-jekyll.html" rel="alternate" type="text/html" title="Welcome to Jekyll!" /><published>2022-05-29T18:38:00+08:00</published><updated>2022-05-29T18:38:00+08:00</updated><id>https://savemejeff.github.io//jekyll/update/2022/05/29/welcome-to-jekyll</id><content type="html" xml:base="https://savemejeff.github.io//jekyll/update/2022/05/29/welcome-to-jekyll.html"><![CDATA[<p>You’ll find this post in your <code class="language-plaintext highlighter-rouge">_posts</code> directory. Go ahead and edit it and re-build the site to see your changes. You can rebuild the site in many different ways, but the most common way is to run <code class="language-plaintext highlighter-rouge">jekyll serve</code>, which launches a web server and auto-regenerates your site when a file is updated.</p>

<p>Jekyll requires blog post files to be named according to the following format:</p>

<p><code class="language-plaintext highlighter-rouge">YEAR-MONTH-DAY-title.MARKUP</code></p>

<p>Where <code class="language-plaintext highlighter-rouge">YEAR</code> is a four-digit number, <code class="language-plaintext highlighter-rouge">MONTH</code> and <code class="language-plaintext highlighter-rouge">DAY</code> are both two-digit numbers, and <code class="language-plaintext highlighter-rouge">MARKUP</code> is the file extension representing the format used in the file. After that, include the necessary front matter. Take a look at the source for this post to get an idea about how it works.</p>

<p>Jekyll also offers powerful support for code snippets:</p>

<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">def</span> <span class="nf">print_hi</span><span class="p">(</span><span class="nb">name</span><span class="p">)</span>
  <span class="nb">puts</span> <span class="s2">"Hi, </span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="s2">"</span>
<span class="k">end</span>
<span class="n">print_hi</span><span class="p">(</span><span class="s1">'Tom'</span><span class="p">)</span>
<span class="c1">#=&gt; prints 'Hi, Tom' to STDOUT.</span></code></pre></figure>

<p>Check out the <a href="https://jekyllrb.com/docs/home">Jekyll docs</a> for more info on how to get the most out of Jekyll. File all bugs/feature requests at <a href="https://github.com/jekyll/jekyll">Jekyll’s GitHub repo</a>. If you have questions, you can ask them on <a href="https://talk.jekyllrb.com/">Jekyll Talk</a>.</p>]]></content><author><name></name></author><category term="jekyll" /><category term="update" /><summary type="html"><![CDATA[You’ll find this post in your _posts directory. Go ahead and edit it and re-build the site to see your changes. You can rebuild the site in many different ways, but the most common way is to run jekyll serve, which launches a web server and auto-regenerates your site when a file is updated. Jekyll requires blog post files to be named according to the following format: YEAR-MONTH-DAY-title.MARKUP Where YEAR is a four-digit number, MONTH and DAY are both two-digit numbers, and MARKUP is the file extension representing the format used in the file. After that, include the necessary front matter. Take a look at the source for this post to get an idea about how it works. Jekyll also offers powerful support for code snippets: def print_hi(name) puts "Hi, #{name}" end print_hi('Tom') #=&gt; prints 'Hi, Tom' to STDOUT. Check out the Jekyll docs for more info on how to get the most out of Jekyll. File all bugs/feature requests at Jekyll’s GitHub repo. If you have questions, you can ask them on Jekyll Talk.]]></summary></entry></feed>