C++ new 分配的内存一定在堆上吗?

最近我突然想到 placement new 可以直接在指定位置直接构造对象而不分配内存,于是有了这篇文章。

1. 一般情况下,new 在堆上分配内存

在大多数情况下,new 确实会在 堆(heap) 上分配内存:

int* p = new int(42); // 在堆上分配内存
  • 堆内存 由 C++ 运行时库(如 malloc/free)管理。
  • 必须手动释放,否则会导致内存泄漏:
    delete p; // 释放堆内存

2. new 还可以在栈上分配内存

C++ 提供了 placement new(定位 new),允许在指定的内存地址上构造对象,而 不分配新内存。因此,我们可以让 new 在栈上分配内存:

#include <new>

void demo() {
    alignas(int) char buffer[sizeof(int)]; // 栈上的内存
    int* p = new (buffer) int(42); // 在栈上构造 int

    std::cout << *p << std::endl; // 输出 42

    // 不需要 delete,但要手动调用析构函数(如果是类对象)
    p->~int(); // 对于 int 这种 trivial 类型,可以省略
}

关键点

  • buffer 是栈上的数组,new (buffer) int 只是在该地址构造对象。
  • 不能使用 delete p,因为内存是栈上的,delete 会尝试释放堆内存,导致未定义行为(UB)。
  • 必须确保内存对齐(如 alignas(int)),否则某些 CPU 架构(如 ARM)会崩溃。

3. new 还可以在其他存储区域分配内存

除了堆和栈,new 还可以在 自定义内存池共享内存硬件寄存器映射内存 上分配内存,只要提供正确的地址即可。

示例:在静态存储区分配

#include <new>

char static_buffer[sizeof(int)]; // 静态存储区(全局变量)

int main() {
    int* p = new (static_buffer) int(100); // 在静态区构造对象
    std::cout << *p << std::endl;
    p->~int();
    return 0;
}
  • static_buffer 位于程序的 数据段(data segment),而不是堆或栈。

示例:在内存池中分配

#include <new>
#include <vector>

class MemoryPool {
public:
    void* allocate(size_t size) {
        return pool.data(); // 返回内存池地址
    }
private:
    std::vector<char> pool{1024}; // 模拟内存池
};

int main() {
    MemoryPool pool;
    int* p = new (pool.allocate(sizeof(int))) int(200); // 在内存池构造
    std::cout << *p << std::endl;
    p->~int();
    return 0;
}
  • 这里 new 在自定义的内存池上分配,而不是堆或栈。

4. 为什么需要 placement new

(1)避免堆分配的开销

  • 高频小对象(如游戏中的粒子系统)可以使用栈或内存池,减少 new/delete 的代价。

    (2)控制对象的内存位置

  • 嵌入式系统可能需要将对象放在特定地址(如硬件寄存器)。
  • 共享内存(IPC)需要在固定地址构造对象。

    (3)实现自定义内存管理

  • 内存池、对象池等技术依赖 placement new 在预分配的内存上构造对象。

因此,new 分配的内存不一定在堆上,它的行为取决于具体的使用方式。理解这一点有助于优化性能并避免未定义行为。

How to Properly Merge Defaults with User Settings in JavaScript

When working with JavaScript, we often need to set default configurations and allow users to override certain values. A common but flawed approach is using the logical OR (||) operator for defaults, which can lead to unexpected behavior. This article explains why || is problematic for object merging and how to correctly override defaults using the ES6 spread operator (...).

1. The Problem: Why || Falls Short

Consider this typical pattern:

const enabledEngines = this.settings?.enabledEngines || {
    baidu: true,
    google: true,
    bing: true,
    so: true,
    sogou: true
};

Key Issues with ||:

  1. || Only Checks for Falsy Values, Failing to Override false

    • If this.settings?.enabledEngines is { google: false }, the || operator won’t apply the fallback (since an object is truthy), incorrectly keeping google: true.
    • The fallback only triggers if the left side is undefined/null.
  2. No Partial Overrides

    • If a user only wants to disable one engine (e.g., { bing: false }), || forces a full replacement, discarding other defaults.

2. The Solution: Object Spread Operator (...)

The correct approach is to let user settings override defaults without discarding the entire object. The ES6 spread operator (...) solves this elegantly:

const enabledEngines = {
    baidu: true,
    google: true,
    bing: true,
    so: true,
    sogou: true,
    ...this.settings?.enabledEngines  // User settings override defaults
};

Why This Works:

  1. Correctly Handles false Overrides
    • If the user passes { google: false }, google is properly disabled while other defaults remain.
  2. Supports Partial Updates
    • Users only need to specify changes; unspecified keys keep their defaults.
  3. Safer Than ||
    • Even an empty object {} won’t trigger an unwanted fallback.

3. Real-World Examples

Case 1: User Disables Google

const userSettings = { google: false };

//  Wrong (||)
const badResult = userSettings || { google: true, bing: true };
// => { google: false } ( bing is lost!)

//  Right (...)
const goodResult = { google: true, bing: true, ...userSettings };
// => { google: false, bing: true } (Correct!)

Case 2: No User Settings Provided

const userSettings = undefined;

//  Wrong (||)
const badResult = userSettings || { google: true };
// => { google: true } (Works but inflexible)

//  Right (...)
const goodResult = { google: true, ...userSettings };
// => { google: true } (Same outcome, but consistent)

4. When Is || Acceptable?

While || isn’t ideal for objects, it’s still useful for:

  • Primitive defaults (e.g., string, number, boolean)
    const limit = userLimit || 10;  //  Fails if userLimit is 0!
  • Full replacements (not merging)
    const config = userConfig || defaultConfig;  // Replaces entirely

For object merging, always prefer ....

服务器时间不对,如何处理?

当服务器时间不正确时,可以按照以下步骤排查和修复:


1. 检查当前时间与时区

# 查看当前系统时间
date

# 查看时区(Linux)
timedatectl     # 或使用 `ls -l /etc/localtime`

# 查看时区(Windows)
systeminfo | find "时区"
  • 若时区错误:修正时区(见步骤2)。
  • 若时间错误但时区正确:可能是时间未同步(见步骤3)。

2. 修正时区

Linux

# 列出所有时区(如 Asia/Shanghai)
timedatectl list-timezones

# 设置时区
sudo timedatectl set-timezone Asia/Shanghai

# 手动更新硬件时钟(可选)
sudo hwclock --systohc

Windows

  • 通过图形界面:
    控制面板 > 时钟和区域 > 设置时间和日期 > 更改时区
  • 或使用命令行:
    tzutil /s "China Standard Time"

3. 同步网络时间

Linux(使用 NTP 服务)

# 安装/启动 NTP 服务(根据发行版选择)
sudo apt install ntpdate   # Debian/Ubuntu
sudo yum install ntp       # CentOS/RHEL

# 手动同步时间(以阿里云 NTP 为例)
sudo ntpdate ntp.aliyun.com

# 启用自动同步(使用 systemd-timesyncd 或 chrony)
sudo timedatectl set-ntp true

# 验证同步状态
timedatectl status

Windows

  • 图形界面:
    设置 > 时间和语言 > 日期和时间 > 自动设置时间
  • 或命令行强制同步:
    w32tm /resync

4. 检查硬件时钟(BIOS 时间)

  • Linux

    # 查看硬件时钟时间
    sudo hwclock --show
    
    # 将系统时间写入硬件时钟(解决重启后时间错误)
    sudo hwclock --systohc
  • Windows
    重启进入 BIOS 检查时间是否正确,或使用命令:

    # 同步硬件时钟
    w32tm /resync

5. 其他可能问题

  • 虚拟机时间漂移:安装虚拟机增强工具(如 VMware Tools/VirtualBox Guest Additions)。
  • NTP 服务冲突:确保只启用一个时间同步服务(如 ntpdchrony)。
  • 防火墙限制:确保 UDP 123 端口(NTP 端口)未被拦截。

6. 验证修复

# 再次检查时间
date
timedatectl status   # Linux

如果问题持续,尝试重启服务器或联系运维人员检查硬件时钟电池(CMOS 电池可能耗尽)。


通过以上步骤,绝大多数时间问题可以解决。根据服务器类型(物理机/虚拟机/云服务器)和操作系统选择对应方法。

bash array[@] 语法详解

最近我写的 bash 脚本中需要遍历数组,用到了 bash 中的 array[@] 这种语法。

在 Bash shell 脚本中,array[@] 是一种特殊的数组引用语法,它有以下几个重要特点:

基本含义

  • array[@] 表示引用数组 array 的所有元素
  • @ 是一个特殊的下标,表示"数组中的所有元素"

array[*] 的区别

  • array[@]array[*] 都表示数组的所有元素
  • 关键区别在于它们在引号中的展开方式:
    • "${array[@]}":每个元素保持独立,即使包含空格也会被正确处理
    • "${array[*]}":所有元素合并为一个字符串,用第一个字符的 IFS(内部字段分隔符)连接

使用示例

fruits=("apple" "banana" "cherry")

# 打印所有元素
echo "${fruits[@]}"  # 输出:apple banana cherry

# 在引号中使用
for fruit in "${fruits[@]}"; do
    echo "$fruit"
done
# 输出:
# apple
# banana
# cherry

带空格的元素处理

当数组元素包含空格时,"${array[@]}" 能正确处理:

files=("file 1.txt" "file 2.txt")

# 正确方式 - 每个元素保持独立
for file in "${files[@]}"; do
    echo "$file"
done
# 输出:
# file 1.txt
# file 2.txt

# 错误方式 - 空格会导致元素被拆分
for file in ${files[@]}; do
    echo "$file"
done
# 输出:
# file
# 1.txt
# file
# 2.txt

for ... in 介绍

你可能注意到了,这里面用到了 for ... in 这种循环结构。

介绍数组遍历怎么少的了 for ... in 呢

Bash 中的 for...in 循环是一种常用的循环结构,用于遍历一组值或列表中的元素。

for ... in 基本语法

for variable in list
do
    commands
done

或者写在一行:

for variable in list; do commands; done

for ... in 使用示例

  1. 遍历固定列表

    for i in 1 2 3 4 5
    do
       echo "Number: $i"
    done
  2. 遍历字符串列表

    for color in red green blue
    do
       echo "Color: $color"
    done
  3. 使用通配符遍历文件

    for file in *.txt
    do
       echo "Text file: $file"
    done
  4. 使用命令输出作为列表

    for user in $(cat /etc/passwd | cut -d: -f1)
    do
       echo "User: $user"
    done
  5. 使用范围表达式

    for i in {1..5}
    do
       echo "Number: $i"
    done
  6. 带步长的范围表达式

    for i in {1..10..2}
    do
       echo "Odd number: $i"
    done

for ... in 特殊用法

  • 遍历位置参数

    for arg in "$@"
    do
      echo "Argument: $arg"
    done
  • 遍历数组

    arr=("apple" "banana" "cherry")
    for fruit in "${arr[@]}"
    do
      echo "Fruit: $fruit"
    done

注意事项

  1. list 中的元素默认以空格分隔
  2. 如果元素包含空格,应该用引号括起来
  3. dodone 是必须的关键字
  4. 循环变量不需要提前声明

最近发现一个今天吃什么的在线小工具

人生三大难题:早餐吃什么?午餐吃什么?晚餐吃什么?

你是不是也在为今天吃什么而苦恼?

我有个朋友给我推荐了个网站 https://zaixianjisuanqi.com/jin-tian-chi-shen-me

一看,做的还挺好!

试了一下,可以根据简单的需求来随机生成今天的菜单,感觉还蛮有用的。

你要是也经常做饭,然而不知道该吃什么,那么可以尝试一下这个工具。

php opcache 优化

opcache 优化建议

[Zend Opcache]
zend_extension=/www/server/php/84/lib/php/extensions/no-debug-non-zts-20240924/opcache.so
opcache.enable=1
opcache.memory_consumption=256          ; 增加到256MB,如果应用较大
opcache.interned_strings_buffer=32      ; 保持32MB,对于大型应用足够
opcache.max_accelerated_files=100000    ; 增加到100000,如果文件很多
opcache.revalidate_freq=60              ; 增加到60秒,减少检查频率
opcache.fast_shutdown=1
opcache.enable_cli=1                    ; 如果CLI脚本需要加速则保留
opcache.save_comments=0                 ; 禁用可节省内存
opcache.validate_timestamps=0           ; 生产环境建议禁用,更新后手动重置
opcache.jit_buffer_size=256m            ; 如果使用JIT且内存充足可增加
opcache.jit=1255                         ; 更激进的JIT模式(1255=函数+循环优化)
opcache.huge_code_pages=1               ; 启用大内存页支持(需系统支持)

; 文件缓存相关(可选)
;opcache.file_cache=/tmp/opcache        ; 启用文件缓存可减少内存使用
;opcache.file_cache_only=0              ; 同时使用内存和文件缓存
;opcache.file_cache_consistency_checks=1 ; 文件缓存一致性检查

extension=/www/server/php/84/lib/php/extensions/no-debug-non-zts-20240924/igbinary.so

关键调整说明:

  1. 生产环境建议

    • 设置 opcache.validate_timestamps=0 并在部署时手动清除缓存
    • 禁用 opcache.save_comments 可节省约5-10%内存
  2. JIT优化

    • 模式从1205改为1255可启用更全面的优化
    • 缓冲区增加到256MB(如果服务器内存充足)
  3. 性能提升

    • opcache.huge_code_pages=1 可提升性能(需系统配置大内存页)
    • 考虑启用文件缓存减少内存压力
  4. 监控建议

    • 使用 opcache_get_status() 监控内存使用情况
    • 确保 opcache.memory_consumption 不会频繁达到上限

opcache.revalidate_freq 含义

opcache.revalidate_freq=60 这个配置项控制的是 OPcache 检查PHP脚本是否被修改的时间间隔(单位为秒)。以下是详细解释:

作用原理

  1. 检查机制

    • 当这个值设为60时,OPcache会每隔60秒检查一次被缓存的PHP脚本文件是否在磁盘上被修改过
    • 如果发现文件被修改,会自动重新缓存新版本的文件
  2. validate_timestamps 的关系

    • 这个配置只有opcache.validate_timestamps=1(默认值)时才生效
    • 如果设置 validate_timestamps=0,则完全禁用检查,revalidate_freq 会失效

生产环境建议

场景 推荐配置 原因
开发环境 revalidate_freq=2 + validate_timestamps=1 需要频繁看到代码改动效果
生产环境 validate_timestamps=0 + 部署时手动重置 完全避免检查开销,最高性能
折中方案 revalidate_freq=60 + validate_timestamps=1 平衡性能与实时性

性能影响

  1. 设置较高值(如60)的优点

    • 减少磁盘I/O操作(每次检查都需要stat()系统调用)
    • 提升约5-10%的请求处理速度(测试数据)
  2. 潜在风险

    • 代码更新后最长需要等待60秒才能生效
    • 可通过opcache_reset()强制刷新缓存

最佳实践

  1. 生产环境建议禁用时间戳验证(validate_timestamps=0),通过部署脚本在更新后执行:
    sudo service php-fpm reload
  2. 如果必须启用检查,建议值:
    • 高流量站点:≥300秒
    • 普通站点:60-120秒
    • 开发环境:1-5秒

这个配置的调整需要权衡实时性性能,根据您的部署流程选择最适合的方案。

C++打印任意proto消息方法

在打印proto时,总需要将pb文件加入源代码,然后编译,然后再解析,实在太麻烦。

能不能直接根据proto文件直接输出明文消息呢?实际上是可以的!

void GetMessageTypeFromProtoFile(
        const std::string& proto_filename,
        google::protobuf::FileDescriptorProto* file_desc_proto) {
    using namespace google::protobuf;
    using namespace google::protobuf::io;
    using namespace google::protobuf::compiler;

    FILE* proto_file = fopen(proto_filename.c_str(), "r");
    {
        if (proto_file == NULL) {
            LOG(FATAL) << "Cannot open .proto file: " << proto_filename;
        }

        FileInputStream proto_input_stream(fileno(proto_file));
        Tokenizer tokenizer(&proto_input_stream, NULL);
        Parser parser;
        if (!parser.Parse(&tokenizer, file_desc_proto)) {
            LOG(FATAL) << "Cannot parse .proto file:" << proto_filename;
        }
    }
    fclose(proto_file);

    // Here we walk around a bug in protocol buffers that
    // |Parser::Parse| does not set name (.proto filename) in
    // file_desc_proto.
    if (!file_desc_proto->has_name()) {
        file_desc_proto->set_name(proto_filename);
    }
}

这个函数的输入是一个 .proto 文件的文件名。输出是一个 FileDescriptorProto 对象。这个对象里存储着对 .proto 文件解析之后的结果。我们接下来用这些结果动态生成某个 protocol message 的 instance(或者用C++术语叫做object)。然后可以调用这个 instance 自己的 ParseFromArray/String 成员函数,来解析数据文件中的每一条记录的内容。请看如下代码:

//-----------------------------------------------------------------------------
// Print contents of a record file with following format:
//
//   { <int record_size> <KeyValuePair> }
//
// where KeyValuePair is a proto message defined in mpimr.proto, and
// consists of two string fields: key and value, where key will be
// printed as a text string, and value will be parsed into a proto
// message given as |message_descriptor|.
//-----------------------------------------------------------------------------

void PrintDataFile(
        const std::string& data_filename,
        const google::protobuf::FileDescriptorProto& file_desc_proto,
        const std::string& message_name) {
    const int kMaxRecieveBufferSize = 32 * 1024 * 1024;  // 32MB
    static char buffer&#91;kMaxRecieveBufferSize];

    std::ifstream input_stream(data_filename.c_str());
    if (!input_stream.is_open()) {
        LOG(FATAL) << "Cannot open data file: " << data_filename;
    }

    google::protobuf::DescriptorPool pool;
    const google::protobuf::FileDescriptor* file_desc = pool.BuildFile(file_desc_proto);
    if (file_desc == NULL) {
        LOG(FATAL) << "Cannot get file descriptor from file descriptor"
                   << file_desc_proto.DebugString();
    }

    const google::protobuf::Descriptor* message_desc =
            file_desc->FindMessageTypeByName(message_name);
    if (message_desc == NULL) {
        LOG(FATAL) << "Cannot get message descriptor of message: " << message_name;
    }

    google::protobuf::DynamicMessageFactory factory;
    const google::protobuf::Message* prototype_msg = factory.GetPrototype(message_desc);
    if (prototype_msg == NULL) {
        LOG(FATAL) << "Cannot create prototype message from message descriptor";
    }
    google::protobuf::Message* mutable_msg = prototype_msg->New();
    if (mutable_msg == NULL) {
        LOG(FATAL) << "Failed in prototype_msg->New(); to create mutable message";
    }

    uint32_t proto_msg_size;  // uint32 is the type used in reocrd files.
    for (;;) {
        input_stream.read((char*)&proto_msg_size, sizeof(proto_msg_size));

        if (proto_msg_size > kMaxRecieveBufferSize) {
            LOG(FATAL) << "Failed to read a proto message with size = " << proto_msg_size
                       << ", which is larger than kMaxRecieveBufferSize (" << kMaxRecieveBufferSize
                       << ")."
                       << "You can modify kMaxRecieveBufferSize defined in " << __FILE__;
        }

        input_stream.read(buffer, proto_msg_size);
        if (!input_stream)
            break;

        if (!mutable_msg->ParseFromArray(buffer, proto_msg_size)) {
            LOG(FATAL) << "Failed to parse value in KeyValuePair:" << pair.value();
        }

        std::cout << mutable_msg->DebugString();
    }

    delete mutable_msg;
}

这个函数需要三个输入:
1)数据文件的文件名
2)之前GetMessageTypeFromProtoFile函数返回的FileDescriptorProto对象
3)数据文件中每条记录的对应的protocol message 的名字(注意,一个 .proto 文件里可以定义多个 protocol messages,所以我们需要知道数据记录对应的具体是哪一个 message)。

以上代码中利用了 DescriptorPool 从 FileDescriptorProto 解析出 FileDescriptor(描述 .proto 文件中所有的 messages)。然后用 DynamicMessageFactory 从 FileDescriptor 里找到我们关注的那个 message 的 MessageDescriptor。接下来,我们利用 DynamicMessageFactory 根据 MessageDescriptor 得到一个 prototype message instance。注意,这个 instance 是不能往里面写内容的(immutable)。我们需要调用其 New 成员函数,来生成一个 mutable 的 instance。

有了一个对应数据记录的 message instance,接下来就好办了。我们读取数据文件中的每条记录。注意:此处我们假设数据文件中以此存放了一条记录的长度,然后是记录内容,接下来是第二条记录的长度和内容,以此类推。所以在上述函数中,我们循环的读取记录长度,然后解析记录内容。值得注意的是,解析内容利用的是 mutable message instance 的 ParseFromArrary 函数;它需要知道记录的长度。因此我们必须在数据文件中存储每条记录的长度。

接下来这段程序演示如何调用 GetMessageTypeFromProtoFile 和 PrintDataFile:

int main(int argc, char** argv) {
    std::string proto_filename, message_name;
    std::vector<string> data_filenames;
    ::google::protobuf::FileDescriptorProto file_desc_proto;
    ParseCmdLine(argc, argv, &proto_filename, &message_name, &data_filenames);
    GetMessageTypeFromProtoFile(proto_filename, &file_desc_proto);
    for (int i = 0; i < data_filenames.size(); ++i) {
        PrintDataFile(data_filenames[i], file_desc_proto, message_name);
    }
    return 0;
}

leetcode接雨水

接雨水

这个题目比较火,原因是字节总拿这个题面试。因此做一下这个题。

题目

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

 

示例 1:

输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
输出:6
解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。 

示例 2:

输入:height = [4,2,0,3,2,5]
输出:9

提示:

  • n == height.length
  • 1 <= n <= 2 * 104
  • 0 <= height[i] <= 105

解答思路

  1. 求解所有的雨水数量,可以转换为求每一个柱子上方雨水的量之和。
  2. 每一个柱子上方雨水的量取决于这个柱子的左侧柱子右侧柱子的高度。如果都比当前柱子高,那么这个柱子雨水的量就是 min(左侧高度,右侧高度) - 当前高度。
  3. 最终问题就变成了求左侧柱子的高度和右侧柱子的高度了。

自然就能想到,可以用两个数组,一个记录当前柱子的左侧高度,一个记录当前柱子的右侧高度。最后遍历一下,直接就可以计算出雨水的量。

代码

class Solution {
public:
    int trap(vector<int>& height) {
        int total = height.size();
        std::vector<int> left_max(total, 0);
        std::vector<int> right_max(total, 0);
        for (int i = 1; i < total; i++) {
            left_max[i] = std::max(left_max[i-1], height[i-1]);
        }
        for (int i = total - 2; i > 0; i--) {
            right_max[i] = std::max(right_max[i+1], height[i+1]);
        }
        int res = 0;
        for (int i = 0 ; i < total; i++) {
            int min = std::min(left_max[i], right_max[i]);
            if (min > height[i]) {
                res += min - height[i];
            }
        }
        return res;
    }
};

你可能还喜欢下面这些文章

mysq常用函数大全

很少用到,但是有时候又必须用到,这里收集一下mysql的常用函数一、数学函数ABS(x)   返回x的绝对值BIN(x)   返回x的二进制(OCT返回八进制,HEX返回十六进制)CEILING(x)   返回大于x的最小整数值EXP(x)   返回值e(自然对数的底)的x次方FLOOR(x)   返回小于x的最大整数值GREATEST(x1,x2,...,xn)返回集合中最大的值LEAST(x1,x2,...,xn)      返回集合中最小的值LN(x)                    返回x的自然对数LOG(x,y)返回x的以y为底的对数MOD(x,y)              

memcacheq的安装与使用

1、安装libevent官网:http://www.libevent.org/2、安装 BerkeleyDB官网:http://www.oracle.com/technetwork/products/berkeleydb/downloads/index.html(下载需要登录)安装:安装完成之后:或者:添加:并执行:3、安装 MemcacheQ官网:http://memcachedb.org/memcacheq/测试是否安装成功:4、启动服务建立相关目录:启动服务:参数说明:-d : 以后台服务方式运行-l : 设置监听地址及端口(默认是22201)-A : 数据页大小-H : 数据保存目录-

欧拉计划:找出1000以下3与5的倍数之和

题目如果我们列出10以下的3和5的倍数,我们可以得到3,5,6,9。它们的和为23。请求出1000以下所有的3和5的倍数之和。原文If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23.Find the sum of all the multiples of 3 or 5 below 1000.原文链接https://projecteuler.net/problem=1解答我会用php,pyth

utf8编码原理

在我的程序中,基本都使用utf8来编码(除非历史原因,实在是无法转换)。但我用的php在处理中文语言的时候,总显得有些生硬,总感觉没有处理英文那么流畅。比如为什么统计字符的数目要远大于汉字的个数?为什么截断中文乱码?为什么一串英文所组成的字符串可以使用数组的方式访问但是中文字符串为什么就是乱码?等等等等之类的问题。这一切的一切,都是因为对utf8编码不了解所导致的!虽然我们有mb_string这个扩展的对中文有很友好的支持,但对于编码原理,还是需要好好的了解一下。但对于初学者,我想你未必有耐心看完这篇文章,可以跳过直接看程序实例,这篇文章可以作为实例程序的参考作用。

shell 四则运算

1. 使用expr命令

expr命令可以用于执行基本的算术运算。注意,运算符之间需要有空格。

result=$(expr 5 + 3)
echo $result  # 输出:8

对于乘法,需要使用反斜杠 \* 进行转义,因为 * 在Shell中有特殊的含义(作为通配符)。

result=$(expr 5 * 3)
echo $result  # 输出:15

2. 使用let命令

let命令允许你在Shell中进行算术运算,并直接更新变量的值。

result=5
let result=result+3
echo $result  # 输出:8

3. 使用双括号(( ))

双括号结构允许你进行算术扩展,并且可以直接在条件语句中使用。

result=$((5 + 3))
echo $result  # 输出:8

if ((result > 7)); then
    echo "Result is greater than 7"
fi

4. 使用bc命令

bc是一个任意精度的计算器语言,它可以用于执行复杂的数学运算。

result=$(echo "5 + 3" | bc)
echo $result  # 输出:8

对于浮点运算,bc非常有用,因为它默认支持小数。

result=$(echo "5 / 3" | bc -l)  # -l 选项启用长浮点运算
echo $result  # 输出:1.6666666666666667

5. 使用awk命令

awk是一个强大的文本处理工具,它也可以用来进行数学运算。

result=$(awk 'BEGIN {print 5 + 3}')
echo $result  # 输出:8

选择哪种方法取决于你的具体需求和你所使用的Shell环境。对于简单的整数运算,双括号(( ))通常是最方便和高效的选择。对于更复杂的运算或需要浮点支持的情况,bc可能是一个更好的选择。

你可能还喜欢下面这些文章

php print 这个坑,遇到连接符从右往左执行

在使用php的时候,我很少用到print这个函数,哦,不对,这是一个语言结构,而并非日函数!看一段代码

linux命令:cd命令,改变当前的工作目录

原标题 : 每天一个linux命令(2):cd命令Linux cd 命令可以说是Linux中最基本的命令语句,其他的命令语句要进行操作,都是建立在使用 cd 命令上的。所以,学习Linux 常用命令,首先就要学好 cd 命令的使用方法技巧。命令格式cd 命令功能切换当前目录至dirName常用范例例一:进入系统根目录命令cd /输出# cd /说明:进入系统根目录,上面命令执行完后拿ls命令看一下,当前目录已经到系统根目录了命令cd .. 或者 cd .. //输出:# pwd/opt/soft# cd ..# cd ..//# pwd/说明:进入系统根目录可以使用“ cd .. ”一直退,就

std::endl为什么导致程序变慢

最近在写hadoop的streaming任务,在输出的时候用了std::endl,就像下面这样:运行后发现程序跑的比python还慢,令人费解。我入门C++的时候,输出hello world也是这样写的,有什么问题?于是查了一下std::endl,发现问题挺大。std::endl解释如下:也就是说每次执行到std::endl的时候都会将缓冲区的内容写入到输出的对象中,这样一来速度慢也就不足为奇。性能测试不加std::endl性能高出20倍。如果程序的逻辑十分简单,那么输出字符串的时候最好用"\n"代替加std::endl

linux命令:ls用法,列出文件及目录

原标题:每天一个linux命令(1):ls命令ls 命令是linux下最常用的命令。ls命令就是list的缩写缺省下ls用来打印出当前目录的清单,如果ls指定其他目录那么就会显示指定目录里的文 件及文件夹清单。 通过ls 命令不仅可以查看linux文件夹包含的文件而且可以查看文件权限(包括目录、文件夹、文件权限),查看目录信息等等。ls 命令在日常的linux操作中用的很多!ls命令格式ls  命令功能列出目标目录中所有的子目录和文件。常用参数-a, –all 列出目录下的所有文件,包括以 . 开头的隐含文件-A 同-a,但不列出“.”(表示当前目录)和“..”(表示当前目录的父目录)。

linux命令桌面壁纸

  linux命令桌面壁纸 作为桌面,再也不怕记不住linux命令了

php运算符的优先级

无论在哪个语言,运算符都有优先级,就如四则运算一样。先乘除后加减,但语言的运算可不止四则。代码写的越多,越觉得运算符的优先级重要性(请原谅我是一个野生程序员)。记住运算符的优先级,能让你少些很多括号,也能体现你的水平,别闹笑话了(在有一个加法和一个乘法的式子中,还特地把乘法用括号括起来,这不是闹笑话吗)。更能够让那种无厘头的bug消失!php官方给出的运算符优先级表(从上到下,优先级依次降低)结合方向运算符附加信息无clone newclone 和 new左[array()右++ -- ~ (int) (float) (string) (array) (object) (bool) @类型和递

看看php的boolean都有哪些,以及其有趣的用法

在写if的时候,在写while的时候,总感觉不写一个true或者false就难受。是的,你还不明确php里面都有哪些值是true,哪些值是false!除了false的,都是true!php里面为false的东西 布尔值 本身 整型值 0(零) 浮点型值 0.0(零) 空字符串,以及字符串 "0" 不包括任何元素的数组 不包括任何成员变量的对象(仅 PHP 4.0 适用) 特殊类型 NULL(包括尚未赋值的变量) 从空标记生成的 SimpleXML 对象 既然说了除了false以外的都是true了,那么就别天真的以为-1是false了,它也是true。有趣的boo

python学习笔记:二、 流程控制

根据如何学习一门新的语言这篇文章的指导,现在的步骤就是学习这门语言的流程控制了。通常来讲,流程控制有顺序,选择,循环这几种。python也不例外,也有这三种流程控制。下面的一一学习。顺序顺序语句很简单,从上往下依次顺序执行。如:a = 1b = 2print(a+b)上面的语句就是顺序执行的语句。选择选择通常使用if和switch,但python中只有if这一个选择语句。语法是:if 满足条件1: 语句1elif 不满足条件1满足1条件2 语句2else 都不满足 语句3比如用一个实际的例子a = 1b = 2if a > b: print('a>b')el

Shell中的条件判断语句if的用法

shell中的if语法是最让我头疼的语法之一,它的判断就向使用USB插头一样——拿起来插入不行,翻转再插入还不行,再翻转插入行了!为了搞清楚这部分语言,我收集了一些文章关于if条件判断的用法,希望对你也有些帮助。一、基本语法语法说明bash shell会按顺序执行if语句,如果command执行后且它的返回状态是0,则会执行符合该条件执行的语句,否则后面的命令不执行,跳到下一条命令。当有多个嵌套时,只有第一个返回0退出状态的命令会导致符合该条件执行的语句部分被执行,如果所有的语句的执行状态都不为0,则执行else中语句。返回状态:最后一个命令的退出状态,或者当没有条件是真的话为0。注意:1、表

计算机语言学习指南

这篇文章讨论基于语言的基本要素,如何快速入门一种计算机语言。是一篇语言从学习到使用的指导手册,并且这种学习方式是一个系统的学习,相比于碎片化的学习,这种学习更加不容易遗忘。语言的基本成分语言的基本成分为数据、运算、控制、传输。想想你学过的语言,是不是都是这样。归结语言的组成成分,学习一门语言可以从这四个方面下手,这四个方面掌握之后,对这个语言就有个最基本的了解了。语言基本成分:数据数据是程序操作的对象。实际上我们可以思考,一个数据拥有的属性有哪些,根据我们已经掌握的语言来说(比如PHP)。$a = 1$a是数据,那么这个数据有哪些属性呢?名称(a),类型(int)。从这一行代码只能发现这两个属

c++ vector取最后一个元素

在C++中,你可以使用std::vector的成员函数back()来获取最后一个元素。这个函数返回对向量中最后一个元素的引用。以下是一个简单的示例:

#include <iostream>
#include <vector>

int main() {
    std::vector<int> vec = {1, 2, 3, 4, 5};

    // 检查向量是否为空
    if (!vec.empty()) {
        // 获取并打印最后一个元素
        int last_element = vec.back();
        std::cout << "The last element is: " << last_element << std::endl;
    } else {
        std::cout << "The vector is empty." << std::endl;
    }

    return 0;
}

在这个例子中,我们创建了一个包含五个整数的std::vector。然后,我们使用back()函数获取最后一个元素,并将其存储在last_element变量中。最后,我们打印出这个元素。

请注意,如果向量是空的(即,不包含任何元素),调用back()函数将导致未定义行为。因此,在调用back()之前,最好先检查向量是否为空,这可以通过调用empty()成员函数来完成。

你可能还喜欢下面这些文章

C++实现python字符串的endswith方法

可以使用的或方法配合比较运算符来模拟方法的功能。下面是一个示例函数,它检查一个字符串是否以另一个字符串结束:在这个示例中,函数接受两个参数:和。函数首先检查的长度是否大于或等于的长度。如果不是,那么显然不能以结束,函数返回。否则,函数使用方法从的末尾提取与长度相同的子字符串,并将其与进行比较。如果它们相等,那么以结束,函数返回。否则,函数返回。请注意,这个函数是区分大小写的。如果你想要一个不区分大小写的版本,你可以在比较之前使用和函数将和转换为小写。在这个版本中,函数首先使用和函数将和转换为小写。然后,它调用函数来检查转换后的字符串是否以结束。

Go入门:三、函数的声明和调用

这是我Go学习笔记的第三篇!接下来学习的是Go的函数声明和调用。我的语言学习过程一般分为下面几个:1. 变量和数据类型2. 流程控制方法3. 函数声明和调用4. 面向对象5. 语言特性6. 标准库函数声明func 函数名称(参数表) 返回值类型 { // 函数体}写一个函数是非常简单的,掌握语法格式就可以了。函数是一个功能的封装,能让函数体内的代码得到很好的复用。比如我要输出个人信息,我可以把个人信息封装到函数里面,后续直接调用这个函数而不是每次都print一堆信息了上面定义的函数没有参数,也没有返回值,非常简单的一个函数。如果我想让姓名可变,那么可以定义一个带有参数的函数接下来定义一个有

python中[:-1]是什么意思

在 Python 中, 是一个列表或字符串的切片操作,用于获取从开始到倒数第二个元素(或字符)的子序列。对于列表:对于字符串:这里的关键是切片操作中的 后面的 ,它表示从末尾开始计数的第一个元素之前的所有元素。具体来说, 是最后一个元素, 是倒数第二个元素,依此类推。因此, 或 都会返回从开始到倒数第二个元素的子序列。

C++入门:三、函数

这是我学习C++的第三篇笔记,函数。我的学习路径是现在学习的是函数的声明、定义、调用等相关知识。函数声明和定义函数的声明包含返回类型,函数名字,0个或者多个形参,无函数体,通常在头文件中对函数进行声明。函数的定义包含返回类型,函数名字,0个或多个形参,以及函数体。比如写一个求阶乘的函数,可以写成下面这样写一些简单的函数大多数语言都差不多,不过可惜每种语言或多或少都有自己的特色,这是比较令人头秃的地方。函数的参数函数可以带有0或多个参数,每个参数都需要声明类型。参数传递可以传值和传引用。如果形参是引用类型,那么它将绑定到对应的实参中,我们成为传引用。否则,将会把实参的值拷贝后赋值给形参,我们成为

python学习笔记:三、函数

这是第三篇python学习笔记,我们即将要学习python的函数。内容主要包括两个部分,函数的声明和函数的调用。函数声明和调用比如我们要声明一个“吃”的函数,语法如下:def eat(): return "eat something"print(eat())上面是一个没有参数的函数,做的事情很简单,声明一个函数,然后返回一个字符串。接下来要增加一个参数了。def ead(food): return "eat %s" % foodprint(eat('fruit'))可以看到,上面声明了一个带有一个参数的函数,当然可以声明带两个,三个等。这些都是固定的,那么如果要声明一个不固定参数的

Go语言的 make 和 new

new 和 make 是两个内置函数,主要用来创建并分配类型的内存。在我们定义变量的时候,可能会觉得有点迷惑,不知道应该使用哪个函数来声明变量,其实他们的规则很简单,new 只分配内存,make 只能用于 slice、map 和 channel 的初始化。下面我们就来具体介绍一下new在Go语言中,new 函数描述如下:从上面的代码可以看出,new 函数只接受一个参数,这个参数是一个类型,并且返回一个指向该类型内存地址的指针。同时 new 函数会把分配的内存置为零,也就是类型的零值。【示例】使用 new 函数为变量分配内存空间。当然,new 函数不仅仅能够为系统默认的数据类型,分配空间,自定义

signal函数详解

signal作用是为信号注册一个处理器。这里的“信号”是软中断信号,这种信号来源主要有三种:程序错误:比如除0,非法内存访问。外部信号:终端Ctrl-C产生的SIGINT信号,定时器产生的SIGALERM。显示请求:kill函数发送的任意信号。当kill一个进程的时候,默认会发送SIGTERM信号,此时这个信号只有默认处理操作(SIG_DFL),直接中断进程执行。如果此时该进程正在执行一个任务,直接终止该进程会导致任务没有完成。这个时候为SIGTERM信号注册一个信号处理函数就十分有必要。介绍参数sig要设置信号处理函数的信号。它可以是实现定义值或下例值之一:SIGABRTSIGFPESIGI

计算机语言学习指南

这篇文章讨论基于语言的基本要素,如何快速入门一种计算机语言。是一篇语言从学习到使用的指导手册,并且这种学习方式是一个系统的学习,相比于碎片化的学习,这种学习更加不容易遗忘。语言的基本成分语言的基本成分为数据、运算、控制、传输。想想你学过的语言,是不是都是这样。归结语言的组成成分,学习一门语言可以从这四个方面下手,这四个方面掌握之后,对这个语言就有个最基本的了解了。语言基本成分:数据数据是程序操作的对象。实际上我们可以思考,一个数据拥有的属性有哪些,根据我们已经掌握的语言来说(比如PHP)。$a = 1$a是数据,那么这个数据有哪些属性呢?名称(a),类型(int)。从这一行代码只能发现这两个属

为什么C++模板不支持分离式编译

前言最近编译C++代码时出现链接失败信息,类似下图:图一初见这个错误有些令人费解,不过经过一番分析,发现原因还是清晰的,和大家一起分享一下。图一中使用的tpl.h代码如下:tpl.cpp代码如下:main.cpp代码如下:我一直习惯把模板实现写在头文件中,因此从未遇见这种错误。这次偶然将模板声明和实现分离,出现了链接错误。这引发了我一些思考,为什么模板不支持分离式编译?模板的编译为了搞清楚模板是怎么编译的,这里以上述tpl.cpp中的compare模板函数为例。tpl.h中声明了一个模板函数并且在tpl.cpp中实现这个模板函数。现在我们编译tpl.cpp,生成的汇编代码如下:图二可以看出,一

C++ any容器的介绍与简易实现

一、any容器是什么?1、any“不是”模板类,any是一种很特殊的容器。2、any只能容纳一个元素,但这个元素可以是任意的类型,可以是基本数据类型(int、double、string、标准容器或者任何自定义类型)。3、一种动态(类型检查只发生在运行时)语言特性的数据结构。4、C++17引入,需要RIIT支持,VS默认是没有支持C++17的,需要自己修改设置,如果不能使用any,请修改标准。二、any类摘要C++typeid关键字详解:三、any类用法注意:any的析构函数删除内部holder对象。如果类型是指针,any并不会对指针执行delete操作,所有any保存原始指针对造成内存泄漏。完