主頁 > 後端開發 > SMFL 教程&個人筆記(2)

SMFL 教程&個人筆記(2)

2022-05-30 15:53:51 後端開發

本文大部分來自官方教程的Google翻譯 但是加了一點點個人的理解和其他相關知識

轉載請注明 原文鏈接 :https://www.cnblogs.com/Multya/p/16317401.html

官方教程: https://www.sfml-dev.org/tutorials/2.5/

本文有什么

這是SFML官方教程的翻譯

涉及的模塊有

  • Audio module 音樂模塊

  • 并沒有網路模塊 作者太懶了

好 開始吧

播放聲音和音樂

聲音還是音樂?

SFML 提供了兩個用于播放音頻的類:sf::Soundsf::Music. 它們都提供或多或少相同的功能,主要區別在于它們的作業方式,(記得#include <SFML/Audio.hpp> 喂)

sf::Sound是一個輕量級物件,用于播放從sf::SoundBuffer. 它應該用于可以存盤在記憶體中的小聲音,并且在播放時應該沒有延遲,例如槍聲、腳步聲等,

sf::Music不會將所有音頻資料加載到記憶體中,而是從源檔案動態流式傳輸,它通常用于播放持續幾分鐘的壓縮音樂,否則將需要幾秒鐘才能加載并占用數百 MB 的記憶體,

加載和播放聲音

如上所述,聲音資料不是直接存盤sf::Sound在一個名為sf::SoundBuffer. 這個類封裝了音頻資料,它基本上是一個 16 位有符號整數陣列(稱為“音頻樣本”),樣本是聲音信號在給定時間點的幅度,因此樣本陣串列示完整的聲音,

事實上,sf::Sound/sf::SoundBuffer類的作業方式 與圖形模塊中的sf::Sprite/sf::Texture相同,因此,如果您了解精靈和紋理如何協同作業,您可以將相同的概念應用于聲音和聲音緩沖區,

您可以使用其loadFromFile功能從磁盤上的檔案加載聲音緩沖區:

#include <SFML/Audio.hpp>

int main()
{
    sf::SoundBuffer buffer;
    if (!buffer.loadFromFile("sound.wav"))
        return -1;

    ...

    return 0;
}

loadFromMemory與其他所有內容一樣,您也可以從記憶體(loadFromMemory) 或自定義輸入流(loadFromStream)加載音頻檔案,

SFML 支持音頻檔案格式 WAV, OGG/Vorbis 和 FLAC,由于許可問題,支持 MP3,

您還可以直接從樣本陣列加載聲音緩沖區,以防它們來自其他來源:

std::vector<sf::Int16> samples = ...;
buffer.loadFromSamples(&samples[0], samples.size(), 2, 44100);

由于loadFromSamples加載的是樣本的原始陣列而不是音頻檔案,因此它需要額外的引數才能獲得對聲音的完整描述,第一個(第三個引數)是通道數;1 聲道定義單聲道,2 聲道定義立體聲,等等,第二個附加屬性(第四個引數)是采樣率;它定義了每秒必須播放多少個樣本才能重建原始聲音,

關于音頻的引數可以參考這個博文: https://blog.csdn.net/fuhanghang/article/details/123730503

可以看出SFML默認的采樣位數也是16bits 所以只用附加通道數和采樣率就能算出所有東西了

現在已經加載了音頻資料,我們可以用一個sf::Sound實體來播放它,

sf::SoundBuffer buffer;
// load something into the sound buffer...

sf::Sound sound;
sound.setBuffer(buffer);
sound.play();

很酷的是,如果您愿意,您可以將相同的聲音緩沖區分配給多個聲音,您甚至可以毫無問題地一起玩它們,

聲音(和音樂)在單獨的執行緒中播放,這意味著你可以在呼叫后自由地做任何你想做的事情play()(當然除了破壞聲音或其資料),聲音將繼續播放直到它完成或明確停止,

播放音樂

sf::Sound不同的是,sf::Music它不預加載音頻資料,而是直接從源流式傳輸資料,音樂的初始化因此更直接:

sf::Music music;
if (!music.openFromFile("music.ogg"))
    return -1; // error
music.play();

需要注意的是,與所有其他 SFML 資源不同,加載函式loadFromFile被命名openFromFile. 這是因為音樂并沒有真正加載,這個功能只是打開它,資料僅在稍后播放音樂時加載,它還有助于記住,只要播放音頻檔案,它就必須保持可用,
的其他加載函式sf::Music遵循相同的約定:如 openFromMemory, openFromStream.

下一步是什么?

現在您可以加載和播放聲音或音樂,讓我們看看您可以用它做什么,

要控制播放,可以使用以下功能:

  • play 開始或恢復播放
  • pause 暫停播放
  • stop 停止播放和倒帶
  • setPlayingOffset 改變當前播放位置

例子:

// start playback
sound.play();

// advance to 2 seconds
sound.setPlayingOffset(sf::seconds(2.f));

// pause playback
sound.pause();

// resume playback
sound.play();

// stop playback and rewind
sound.stop();

getStatus函式回傳聲音或音樂的當前狀態,您可以使用它來知道它是停止、播放還是暫停,

聲音和音樂播放也由一些屬性控制,這些屬性可以隨時更改,

音高(pitch) 是改變聲音感知頻率的一個因素:大于 1 以較高的音高播放聲音,小于 1 以較低的音高播放聲音,1 保持不變,改變音高有一個副作用:它會影響播放速度,

sound.setPitch(1.2f);

音量(volume) 是……音量 ,值范圍從 0(靜音)到 100(全音量),默認值為 100,這意味著您不能發出比其初始音量更大的聲音,

sound.setVolume(50.f);

回圈(loop) 屬性控制聲音/音樂是否自動回圈播放 ,如果它回圈播放,它將在完成后從頭開始播放,一次又一次,直到您明確呼叫stop. 如果不設定回圈,它會在完成后自動停止,

sound.setLoop(true);

更多屬性可用,但它們與3D化相關,并在相應教程中進行了說明,

常見錯誤

損壞的聲音緩沖區

最常見的錯誤是讓聲音緩沖區超出作用域(因此被破壞),而聲音仍在使用它,

sf::Sound loadSound(std::string filename)
{
    sf::SoundBuffer buffer; // this buffer is local to the function, it will be destroyed...
    buffer.loadFromFile(filename);
    return sf::Sound(buffer);
} // ... here

sf::Sound sound = loadSound("s.wav");
sound.play(); // ERROR: the sound's buffer no longer exists, the behavior is undefined

請記住,聲音只保留指向 您提供給它的聲音緩沖區的指標,它不包含自己的副本,您必須正確管理聲音緩沖區的生命周期,以便它們在被聲音使用時保持活動狀態,

太多的聲音

另一個錯誤來源是當您嘗試創建大量聲音時,SFML 內部有限制;它可能因作業系統而異,但不應超過 256,此限制是可以同時存在的sf::Sound實體數和sf::Music實體數之和,保持低于限制的一個好方法是在不再需要時銷毀(或回收)未使用的聲音,當然,這只適用于您必須管理大量聲音和音樂的情況,

在播放時破壞音樂源

請記住,只要播放音樂,它就需要它的來源,當您的應用程式播放它時,磁盤上的音樂檔案可能不會被洗掉或移動,但是當您從記憶體中的檔案或自定義輸入流中播放音樂時,事情會變得更加復雜:

// we start with a music file in memory (imagine that we extracted it from a zip archive)
std::vector<char> fileData = https://www.cnblogs.com/Multya/archive/2022/05/29/...;

// we play it
sf::Music music;
music.openFromMemory(&fileData[0], fileData.size());
music.play();

//"ok, it seems that we don't need the source file any longer"
fileData.clear();

// ERROR: the music was still streaming the contents of fileData! The behavior is now undefined

sf::Music 不可復制

最后的“錯誤”是一個提醒:sf::Music類是不可復制的,所以你不會被允許這樣做:

sf::Music music;
sf::Music anotherMusic = music; // ERROR

void doSomething(sf::Music music)
{
    ...
}
sf::Music music;
doSomething(music); // ERROR (the function should take its argument by reference, not by value)

錄制音頻

錄制到聲音緩沖區

捕獲的音頻資料最常見的用途是將其保存到聲音緩沖區 ( sf::SoundBuffer) 中,以便可以播放或保存到檔案中,

這可以通過sf::SoundBufferRecorder類的非常簡單的介面來實作:

// first check if an input audio device is available on the system
if (!sf::SoundBufferRecorder::isAvailable())
{
    // error: audio capture is not available on this system
    ...
}

// create the recorder
sf::SoundBufferRecorder recorder;

// start the capture
recorder.start();

// wait...

// stop the capture
recorder.stop();

// retrieve the buffer that contains the captured audio data
const sf::SoundBuffer& buffer = recorder.getBuffer();

靜態函式檢查系統SoundBufferRecorder::isAvailable是否支持錄音,如果回傳false,您將根本無法使用sf::SoundBufferRecorder該類,

和函式是不言自明的startstop捕獲在其自己的執行緒中運行,這意味著您可以在開始和停止之間做任何您想做的事情,捕獲結束后,錄制的音頻資料可在聲音緩沖區中使用,您可以使用該 getBuffer函式獲取該緩沖區,

使用記錄的資料,您可以:

  • 將其保存到檔案

    buffer.saveToFile("my_record.ogg");
    
  • 直接播放

    sf::Sound sound(buffer);
    sound.play();
    
  • 訪問原始音頻資料并對其進行分析、轉換等,

    const sf::Int16* samples = buffer.getSamples();
    std::size_t count = buffer.getSampleCount();
    doSomething(samples, count);
    

如果您想在錄音機銷毀或重新啟動后使用捕獲的音頻資料,請不要忘記制作緩沖區的副本

選擇輸入設備

如果您有多個聲音輸入設備連接到您的計算機(例如麥克風、聲音介面(外部聲卡)或網路攝像頭麥克風),您可以指定用于錄制的設備,聲音輸入設備由其名稱標識,可通過靜態函式SoundBufferRecorder::getAvailableDevices()獲得包含所有連接設備名稱的std::vector<std::string>,然后,您可以通過將所選設備名稱傳遞給setDevice()方法,從串列中選擇一個設備進行錄制,甚至可以即時更改設備(即在錄制時),

當前使用的設備名稱可以通過呼叫獲取getDevice(),如果您不自己選擇設備,則將使用默認設備,它的名字可以通過靜態SoundBufferRecorder::getDefaultDevice()函式獲得,

下面是一個如何設定輸入設備的小例子:

// get the available sound input device names
std::vector<std::string> availableDevices = sf::SoundRecorder::getAvailableDevices();

// choose a device
std::string inputDevice = availableDevices[0];

// create the recorder
sf::SoundBufferRecorder recorder;

// set the device
if (!recorder.setDevice(inputDevice))
{
    // error: device selection failed
    ...
}

// use recorder as usual

自定義錄制

如果您不希望將捕獲的資料存盤在聲音緩沖區中,您可以撰寫自己的錄音機,這樣做將允許您在捕獲音頻資料時(幾乎)直接從錄音設備進行處理,例如,通過這種方式,您可以通過網路傳輸捕獲的音頻,對其執行實時分析等,

要撰寫自己的記錄器,您必須從sf::SoundRecorder抽象基類繼承,實際上, sf::SoundBufferRecorder只是這個類的一個內置特化,

您只有一個虛函式可以在派生類中覆寫:onProcessSamples. 每次捕獲新的音頻樣本塊時都會呼叫它,因此這是您實作特定內容的地方,

默認情況下,音頻樣本每 100 毫秒提供給onProcessSamples方法一次,您可以使用該 setProcessingInterval方法更改間隔,例如,如果您想要實時處理記錄的資料,您可能想要使用更小的間隔,請注意,這只是一個提示,實際周期可能會有所不同,因此不要依賴它來實作精確的計時,

還有兩個額外的虛函式可以選擇覆寫:onStartonStop. 它們分別在捕獲開始/停止時呼叫,它們對于初始化/清理任務很有用,

這是一個完整的派生類的骨架:

class MyRecorder : public sf::SoundRecorder
{
    virtual bool onStart() // optional
    {
        // initialize whatever has to be done before the capture starts
        ...

        // return true to start the capture, or false to cancel it
        return true;
    }

    virtual bool onProcessSamples(const sf::Int16* samples, std::size_t sampleCount)
    {
        // do something useful with the new chunk of samples
        ...

        // return true to continue the capture, or false to stop it
        return true;
    }

    virtual void onStop() // optional
    {
        // clean up whatever has to be done after the capture is finished
        ...
    }
}

isAvailable/start/stop函式在基類sf::SoundRecorder中定義 ,start因此在每個派生類中都被繼承,這意味著您可以使用與sf::SoundBufferRecorder類完全相同的任何記錄器類方法 ,

if (!MyRecorder::isAvailable())
{
    // error...
}

MyRecorder recorder;
recorder.start();
...
recorder.stop();

執行緒問題

由于記錄是在一個單獨的執行緒中完成的,因此了解究竟發生了什么以及在哪里發生是很重要的,

onStart將由start函式直接呼叫,因此它在呼叫它的同一執行緒中執行,但是, onProcessSampleonStop始終將從 SFML 創建的內部記錄執行緒呼叫,

如果您的記錄器使用可能在呼叫者執行緒和記錄執行緒中同時訪問的資料您必須保護它(例如使用互斥鎖)以避免并發訪問,這可能導致未定義的行為——損壞的資料被記錄、崩潰等

如果您對執行緒不夠熟悉,可以參考相應的教程了解更多資訊,

自定義音頻流

音頻流?那是什么?

音頻流類似于音樂(還記得sf::Music嗎?),它具有幾乎相同的功能和行為,唯一的區別是音頻流不播放音頻檔案:而是播放您直接提供的自定義音頻源,換句話說,定義自己的音頻流不僅可以播放檔案,還可以播放:通過網路傳輸的聲音、程式生成的音樂、SFML 不支持的音頻格式等,

實際上,sf::Music該類只是一個特化的音頻流,它從檔案中獲取其音頻樣本,

由于我們正在討論流式傳輸,我們將處理無法完全加載到記憶體中的音頻資料,而是在播放時以小塊的形式加載,如果您的聲音可以完全加載并且可以放入記憶體,那么音頻流對您沒有幫助:只需將音頻資料加載到 sf::SoundBuffer并使用常規sf::Sound播放它就可以了,

sf::SoundStream

為了定義自己的音頻流,您需要從sf::SoundStream抽象基類繼承,在派生類中有兩個虛函式需要重寫:onGetDataonSeek.

class MyAudioStream : public sf::SoundStream
{
    bool onGetData(Chunk &data) override ;

    void onSeek(sf::Time timeOffset) override ;
};

onGetData每當音頻樣本用完并需要更多音頻樣本時,基類都會呼叫它,data您必須通過填寫引數來提供新的音頻樣本:

bool MyAudioStream::onGetData(Chunk& data) override
{
    data.samples = /* put the pointer to the new audio samples */;
    data.sampleCount = /* put the number of audio samples available in the new chunk */;
    //將可用音頻采樣個數放入新區塊(trunk)
    return true;
}

當一切正常時, 您必須回傳true,如果必須停止播放,要么是因為發生了錯誤,要么是因為沒有更多的音頻資料可以播放,那就回傳false

SFML 在回傳后立即制作音頻樣本的內部副本onGetData,因此如果您不想保留原始資料,則不必保留,

呼叫公共函式setPlayingOffset時呼叫onSeek函式,其目的是改變源資料中的當前播放位置,該引數是表示新位置的時間值,從聲音的開頭(不是從當前位置開始),這個功能有時是不可能實作的,在這些情況下,將其留空,并告訴使用自定義類的用戶不支持更改播放位置,

void onSeek(sf::Time timeOffset) override
{	// sometimes it may use like this:
    // compute the corresponding sample index according to the sample rate and channel count
    m_currentSample = static_cast<std::size_t>(timeOffset.asSeconds() * getSampleRate() * getChannelCount());
}

// or like this..

void onSeek(sf::Time timeOffset) override
{
    // nothing happen 
    // or maybe some warmings
}

現在你的類幾乎可以開始作業了,現在唯一sf::SoundStream需要知道的是流的通道數采樣率,以便可以按預期播放,要讓基類知道這些引數,您必須在流類中知道它們后立即呼叫受保護(protected)的函式initialize (這很可能是在加載/初始化流時),

// where this is done totally depends on how your stream class is designed
unsigned int channelCount = ...;
unsigned int sampleRate = ...;
initialize(channelCount, sampleRate);

執行緒問題

音頻流總是在一個單獨的執行緒中播放,因此了解究竟發生了什么以及在哪里發生是很重要的,

onSeeksetPlayingOffset函式直接呼叫,所以總是在呼叫者執行緒中執行,但是, onGetData只要正在播放流,就會在 SFML 創建的單獨執行緒中重復呼叫該函式,如果您的流使用可能在呼叫者執行緒和播放執行緒中同時訪問的資料您必須保護它(例如使用互斥鎖)以避免并發訪問,這可能導致未定義的行為——損壞的資料被播放、崩潰等

如果您對執行緒不夠熟悉,可以參考相應的教程了解更多資訊,

使用您的音頻流

現在您已經定義了自己的音頻流類,讓我們看看如何使用它,事實上,事情與教程中關于sf::Music的內容非常相似,您可以使用playstopsetPlayingOffsetpause功能控制播放, 您還可以播放聲音的屬性,例如音量或音高,您可以參考 API 檔案或其他音頻教程以獲取更多詳細資訊,

一個簡單的例子

這是一個非常簡單的自定義音頻流類示例,它播放聲音緩沖區的資料,這樣的類可能看起來完全沒用,但這里的重點是關注資料是如何被類流式傳輸的,不管它來自哪里,

#include <SFML/Audio.hpp>
#include <vector>

// custom audio stream that plays a loaded buffer
class MyStream : public sf::SoundStream
{
public:

    void load(const sf::SoundBuffer& buffer)
    {
        // extract the audio samples from the sound buffer to our own container
        m_samples.assign(buffer.getSamples(), buffer.getSamples() + buffer.getSampleCount());

        // reset the current playing position 
        m_currentSample = 0;

        // initialize the base class
        initialize(buffer.getChannelCount(), buffer.getSampleRate());
    }

private:

    bool onGetData(Chunk& data) override
    {
        // number of samples to stream every time the function is called;
        // in a more robust implementation, it should be a fixed
        // amount of time rather than an arbitrary number of samples
        const int samplesToStream = 50000;

        // set the pointer to the next audio samples to be played
        data.samples = &m_samples[m_currentSample];

        // have we reached the end of the sound?
        if (m_currentSample + samplesToStream <= m_samples.size())
        {
            // end not reached: stream the samples and continue
            data.sampleCount = samplesToStream;
            m_currentSample += samplesToStream;
            return true;
        }
        else
        {
            // end of stream reached: stream the remaining samples and stop playback
            data.sampleCount = m_samples.size() - m_currentSample;
            m_currentSample = m_samples.size();
            return false;
        }
    }

    void onSeek(sf::Time timeOffset) override
    {
        // compute the corresponding sample index according to the sample rate and channel count
        m_currentSample = static_cast<std::size_t>(timeOffset.asSeconds() * getSampleRate() * getChannelCount());
    }

    std::vector<sf::Int16> m_samples;
    std::size_t m_currentSample;
};

int main()
{
    // load an audio buffer from a sound file
    sf::SoundBuffer buffer;
    buffer.loadFromFile("sound.wav");

    // initialize and play our custom stream
    MyStream stream;
    stream.load(buffer);
    stream.play();

    // let it play until it is finished
    while (stream.getStatus() == MyStream::Playing)
        sf::sleep(sf::seconds(0.1f));

    return 0;
}

空間化:3D 聲音

介紹

默認情況下,每個揚聲器都會以最大音量播放聲音和音樂;它們沒有空間化

如果聲音是由螢屏右側的物體發出的,您可能希望從右側的揚聲器中聽到它,如果在播放器后面播放音樂,您可能希望從杜比 5.1 音響系統的后置揚聲器聽到它,

如何做到這一點?

空間化的聲音是單聲道的

一個聲音只有當它有一個單一的通道時才能被空間化,即如果它是一個單聲道的聲音,
對于具有更多通道的聲音,空間化被禁用,因為它們已經明確決定如何使用揚聲器,記住這一點非常重要,

偵聽器

音頻環境中的所有聲音和音樂都將被偵聽器(listener)聽到,揚聲器的輸出取決于偵聽器聽到的內容,

定義偵聽器屬性的類是sf::Listener. 由于偵聽器在環境中是唯一的,因此此類僅包含靜態函式,并不打算實體化,

首先,您可以設定偵聽器在場景中的位置:

sf::Listener::setPosition(10.f, 0.f, 5.f);

如果你有一個 2D 世界,你可以在任何地方使用相同的 Y 值,通常是 0,

除了它的位置,您還可以定義偵聽器的方向:

sf::Listener::setDirection(1.f, 0.f, 0.f);

在這里,偵聽器沿 +X 軸定向,這意味著,例如,在 (15, 0, 5) 處發出的聲音將從右揚聲器聽到,

偵聽器的“向上”向量默認設定為 (0, 1, 0),換句話說,偵聽器的頭頂指向 +Y,如果需要,您可以更改“向上”向量,但很少有必要,

sf::Listener::setUpVector(1.f, 1.f, 0.f);

這對應于偵聽器將頭向右傾斜 (+X),

最后,偵聽器可以調整場景的全域音量:

sf::Listener::setGlobalVolume(50.f);

音量的值在 [0 .. 100] 范圍內,因此將其設定為 50 會將其減少到原始音量的一半,

當然,所有這些屬性都可以通過相應的get函式來讀取,

音頻源

SFML 提供的每個音頻源(聲音、音樂、流)都為空間化定義了相同的屬性,

主要屬性是音頻源的位置,

sound.setPosition(2.f, 0.f, -5.f);

默認情況下,此位置是絕對的,但如果需要,它可以相對于偵聽器,

sound.setRelativeToListener(true);

這對于偵聽器自身發出的聲音(如槍聲或腳步聲)很有用,如果將音頻源的位置設定為 (0, 0, 0),它還具有禁用空間化的有趣副作用,在各種情況下都可能需要非空間化的聲音:GUI 聲音(點擊)、環境音樂等,

您還可以根據音頻源與聽眾的距離設定衰減因子,

sound.setMinDistance(5.f);
sound.setAttenuation(10.f);

最小距離(minimum 是以最大音量聽到聲音的距離 ,例如,較大的聲音(例如爆炸聲)應該具有較高的最小距離,以確保從很遠的地方就能聽到它們,請注意,最小距離為 0(聲音在偵聽器的頭部內部!)會導致不正確的空間化并導致聲音不衰減,0 是一個無效值,永遠不要使用它,

衰減(attenuation 是一個乘法因子 ,衰減越大,當聲音遠離偵聽器時聽到的越少,要獲得不衰減的聲音,您可以使用 0,另一方面,使用 100 之類的值會高度衰減聲音,這意味著只有在非常靠近偵聽器時才能聽到聲音,

這是確切的衰減公式,以防您需要準確的值:

MinDistance   is the sound's minimum distance, set with setMinDistance
Attenuation   is the sound's attenuation, set with setAttenuation
Distance      is the distance between the sound and the listener
Volume factor is the calculated factor, in range [0 .. 1], that will be applied to the sound's volume

Volume factor = MinDistance / (MinDistance + Attenuation * (max(Distance, MinDistance) - MinDistance))
  • MinDistance 是聲音的最小距離,用setMinDistance設定
  • Attenuation 是聲音的衰減,用SetAttention設定
  • Distance 是聲音與聽者之間的距離 即主變數
  • Volume factor 是計算出的音量系數,范圍為[0..1],將應用于聲音的音量大小

\(\Large Volume factor = \frac{MinDistance}{ MinDistance + Attenuation(\max{(Distance, MinDistance)} - MinDistance)}\)

可以看出 這是一個從1開始衰減的反比例函式

網路,,?溜了

一般自己用SFML也很少會用到網路 而是選擇用其他更好的庫 所以就不翻網路那塊了

有興趣可以直接去官網找教程~

下次更新實戰了 bb

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/482568.html

標籤:其他

上一篇:【Java面試】JVM如何判斷一個物件可以被回收

下一篇:java學習之MybBaits

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more