Nhờ giãi thích dùm đoạn code (Dim 1 biến là độ dài của 1 String)

Liên hệ QC
Status
Không mở trả lời sau này.

ndu96081631

Huyền thoại GPE
Thành viên BQT
Super Moderator
Tham gia
5/6/08
Bài viết
30,703
Được thích
53,952
Tôi có đoạn code này:
PHP:
Option Explicit
Sub Tach()
   Dim Rng As Range, Clls As Range
   Dim i As Integer
   Dim iText As String
   Application.ScreenUpdating = False
   [A1].CurrentRegion.Copy Destination:=[C1]
   Set Rng = Range("C2:C" & [C1000].End(xlUp).Row)
   Rng.TextToColumns Destination:=[C2], DataType:=xlDelimited, Other:=True, OtherChar:="|"
   [C1].ClearContents
   For Each Clls In [C2].CurrentRegion
       For i = Len(Clls) To 1 Step -1
         iText = Mid(Clls, i, 1)
         If IsNumeric(iText) = False And iText <> "." Then
            With Clls
               .Value = Replace(.Value, iText, "")
               .NumberFormat = "0.00"
            End With
         End If
       Next i
    Next Clls
    Application.ScreenUpdating = True
End Sub
Có cả file đính kèm nữa, xin mời xem qua!
Ở đây tôi chỉ có 1 thắc mắc nhỏ về biến i trong đoạn code trên
Xem code ta thấy rằng biến i chính là độ dài của chuổi trong 1 cell, vậy đương nhiên nó không vượt quá 256 ký tự... Từ suy luận trên tôi Dim i As Byte thấy vừa đủ không thừa không thiếu... Ấy thế mà nó lại báo lổi, trong khi nếu Dim i As Integer thì không có vấn đề
Còn nữa: Nếu vẩn Dim i As Byte và sửa đoạn For i = Len(Clls) To 1 Step -1 thành For i = 1 To Len(Clls) thì hết lổi
Xin vui lòng giãi thích tại sao Dim i As Byte trong code này lại không thể làm việc được với vòng lập quét ngược
Mặc dù sửa lại Dim i As Integer thì code chạy bình thường nhưng tôi cãm thấy ngứa mắt quá ---> Muốn tìm hiểu vấn đề TẠI SAO ở đây! Xin các cao thủ chỉ giáo!
Cãm ơn!
 

File đính kèm

Ở đây tôi chỉ có 1 thắc mắc nhỏ về biến i trong đoạn code trên
Xem code ta thấy rằng biến i chính là độ dài của chuổi trong 1 cell, vậy đương nhiên nó không vượt quá 256 ký tự...

Bác đã có sự nhầm lẫn ạ.

Bill nói :
String Data Type

There are two kinds of strings: variable-length and fixed-length strings.
  • A variable-length string can contain up to approximately 2 billion (2^31) characters.

  • A fixed-length string can contain 1 to approximately 64K (2^16) characters
Và : The codes for String characters range from 0–255. The first 128 characters (0–127) of the character set correspond to the letters and symbols on a standard U.S. keyboard. These first 128 characters are the same as those defined by the ASCII character set. The second 128 characters (128–255) represent special characters, such as letters in international alphabets, accents, currency symbols, and fractions. The type-declaration character for String is the dollar sign ($).


Chắc bác nhầm với dòng :
The codes for String characters range from 0–255.
Thân!
 
Lần chỉnh sửa cuối:
Upvote 0
Bác đã có sự nhầm lẫn ạ.

Bill nói :
String Data Type

There are two kinds of strings: variable-length and fixed-length strings.
  • A variable-length string can contain up to approximately 2 billion (2^31) characters.

  • A fixed-length string can contain 1 to approximately 64K (2^16) characters
Và : The codes for String characters range from 0–255. The first 128 characters (0–127) of the character set correspond to the letters and symbols on a standard U.S. keyboard. These first 128 characters are the same as those defined by the ASCII character set. The second 128 characters (128–255) represent special characters, such as letters in international alphabets, accents, currency symbols, and fractions. The type-declaration character for String is the dollar sign ($).


Chắc bác nhầm với dòng :

Thân!
Xin bạn vui lòng xem lại... Tôi Dim i As Byte không có vấn đề gì nếu như tôi dùng For quét theo chiều thuận... Chỉ khi For có Step -1 thì mới báo lổi
 
Upvote 0
-Đoạn code sau bị báo lỗi Overflow
Mã:
Sub b()
Dim x As Byte
For x = 20 To 1 Step -1
    If Range("A1").Offset(x, 0) = Range("A1").Offset(x - 1, 0) Then
           Range("A1").Offset(x, 0).EntireRow.Delete shift:=xlUp
     End If
Next x
End Sub
-Theo mình, vấn đề nằm ở Step -1. Có lẽ Step -1 đòi hỏi kiểu biến phải có chứa số âm. Nên khi khai báo các kiểu Integer, Long, Single, Currency đều không bị báo lỗi.
 
Upvote 0
-Đoạn code sau bị báo lỗi Overflow
Mã:
Sub b()
Dim x As Byte
For x = 20 To 1 Step -1
    If Range("A1").Offset(x, 0) = Range("A1").Offset(x - 1, 0) Then
           Range("A1").Offset(x, 0).EntireRow.Delete shift:=xlUp
     End If
Next x
End Sub
-Theo mình, vấn đề nằm ở Step -1. Có lẽ Step -1 đòi hỏi kiểu biến phải có chứa số âm. Nên khi khai báo các kiểu Integer, Long, Single, Currency đều không bị báo lỗi.
Thế thì phi phạm bộ nhớ quá nhỉ, có cách nào "vẹn cả đôi đàng" không thầy ơi (em học các sư phụ nên luôn quan niệm tiết kiệm mọi lúc)
Ah... nhân đây em hỏi thêm: Khi em dùng lệnh Set để đặt tên cho 1 vùng, kết thúc quá trình em Set Vung = Nothing để giãi phóng bộ nhớ!
Vậy xin hỏi: tất cả các biến đều cần động tác này hay chỉ riêng biến đối tượng?
 
Upvote 0
Bây giờ chúng ta xem xét về 1 khía cạnh khác đi (tôi thường định hướng 1 vấn đề theo cách: đánh giá xem tính hiệu quả của việc mình đang xem xét, nếu thực sự có tính hiệu quả thì đáng để mổ xẻ xem xét tiếp, còn ngược lại thì dù có "ngứa mắt" vẫn cứ phải theo phương pháp hiệu mà lựa chọn (trong coding thì sự hiệu quả thể hiện ở việc code khoa học và tốc độ phần mềm là đặt lên hàng đầu).

Theo hướng đã nói ở trên, tôi sẽ đi theo khía cạnh khác (thay vì tìm lỗi). Cụ thể là sẽ đặt ra câu hỏi: Tại sao ta lại phải dùng kiểu Byte? Tìm hiểu theo hướng này thì:

Dim i AS Byte
Dim i AS Integer
Dim i AS Long '// thực ra trong tiềm thức của tôi thì luôn luôn có kiểu khai báo này, chả bao giờ dùng kiểu khác cho biến chạy cả (vì đã quá rõ guideline về vấn đề này)

Sau khi VB6 compiled xong cả 3 trường hợp trên thì i đều được chuyển sang Long hết (giá trị sẽ lưu vào 1 thanh ghi giống nhau).

Vậy theo bạn giữa "ngứa mắt" và "tốc độ" (vì rõ ràng nếu khai báo <> Long thì nó lại mất công thêm 1 lệnh convert sang kiểu long khi compiling) thì nên chọn cái nào? Đó là chưa kể chuyện còn có vô số trường hợp chuỗi text (do người dùng gõ vào) có thể có length lớn hơn kiểu byte nhiều ==> error occurred.

Nếu là tôi thì tôi sẽ ko tiếp tục nghiên cứu vấn đề trên mà quyết định chọn ngay hướng hiệu quả: Chọn kiểu Long (fastest) thay vì kiểu Byte cho biến i. Hiện nay, tất cả các vòng For trong code tôi đều chuyển sang biến chạy có Long để đảm bảo 2 nguyên tắc cơ bản trong lập trình: Không lỗi (phải vét cạn trường hợp tối đa và tối thiểu) và tăng tốc độ.

Tiết kiệm bộ nhớ cho mấy cái biến nhỏ xíu ấy làm gì khi mà sau thủ tục đó thì bộ nhớ lưu trữ biến lại được giải phóng hết (mà bộ nhớ thì thừa thãi).

P/S: Theo guideline về tối ưu hóa coding thì trong lập trình chúng ta nên HẠN CHẾ DÙNG VÒNG LẶP FOR EACH (nhưng hình như ở Excel các bạn dùng For Each CtrlName in ... nhiều quá). Còn giải thích tại sao tôi nói những điều trên thì mời các bạn tìm hiểu về tối ưu hóa coding trong VB (VB code optimization)

Cheers!
 
Lần chỉnh sửa cuối:
Upvote 0
Các bạn xem thông tin này trên trang Web của Microsoft: http://support.microsoft.com
PRB: Run-Time Overflow Error w/Byte Type Counter in For Loop
View products that this article applies to.
Article ID : 129709
Last Review : December 9, 2003
Revision : 2.0
This article was previously published under Q129709
On This Page

SYMPTOMS
Using a byte variable as a counter in a For loop with a negative Step value causes an Overflow error at run-time.
Back to the top

CAUSE
In a For loop in Visual Basic for Windows, the Start, Limit and Step values are all coerced to the type of the For loop counter. Since the byte data type cannot be negative, it cannot have a negative step value.
Back to the top

RESOLUTION
This is a limitation of the implementation of the byte datatype and For loop. The solution is to never use byte type variables in For loops where the step is negative or a variable (since the variable may contain a negative number at some future time.)
 
Upvote 0
Dim i AS Byte
Dim i AS Integer
Dim i AS Long '// thực ra trong tiềm thức của tôi thì luôn luôn có kiểu khai báo này, chả bao giờ dùng kiểu khác cho biến chạy cả (vì đã quá rõ guideline về vấn đề này)

Sau khi VB6 compiled xong cả 3 trường hợp trên thì i đều được chuyển sang Long hết (giá trị sẽ lưu vào 1 thanh ghi giống nhau).

Vậy theo bạn giữa "ngứa mắt" và "tốc độ" (vì rõ ràng nếu khai báo <> Long thì nó lại mất công thêm 1 lệnh convert sang kiểu long khi compiling) thì nên chọn cái nào? Đó là chưa kể chuyện còn có vô số trường hợp chuỗi text (do người dùng gõ vào) có thể có length lớn hơn kiểu byte nhiều ==> error occurred.
!
Thật ra mấy món này tôi cũng học được từ các cao thủ trên diển đàn, họ bảo phải khai báo biến vừa đủ để tiết kiệm bộ nhớ (có 1 bài viết về vấn đề này mà tôi quên mất nằm ở đâu rồi)
Vậy theo như những gì bạn vừa nói, nếu biến thuộc kiểu Byte, Integer thì ta nên Dim nó là Long tất tần tật sao? Vì tôi vẩn chưa hiểu ở chổ: Nếu đúng như bạn nói thì kiểu Byte và Integer là dư thừa sao?
Đang tập tành nên còn rất nhiều thứ tôi chưa nắm rõ... Rất mong các bạn hướng dẩn chi tiết
 
Upvote 0
Bạn Ndu ơi,

Biến số Byte theo logic dùng để tính độ rộng trong cột thì rất hợp lý. Tuy nhiên, như anh Voda nói ở trên, khai dim byte không chạy được trong vòng lặp chạy ngược nhưng áp dụng trong loop đếm lên thì OK.

Rất đồng ý với bạn trong việc chọn đúng loại biến số để phù hợp trong từng trường hợp và tiết kiệm tài nguyên. Chỉ nêu biến theo dạng Long khi ta biết rằng biến số > 32k (số chính xác là 32 ngàn mấy trăm gì đó không nhớ rõ lắm).

Còn về lúc nào xóa biến số theo kiểu
thì khi biến là range thì cần làm như vậy trước khi code cấm dứt để lấy lại bộ nhớ. Với các loại biến dim khác thì khỏi khi code chạy xong các biến dim tan tành như mây khói hết.

Mến
 
Upvote 0
Thật ra mấy món này tôi cũng học được từ các cao thủ trên diển đàn, họ bảo phải khai báo biến vừa đủ để tiết kiệm bộ nhớ (có 1 bài viết về vấn đề này mà tôi quên mất nằm ở đâu rồi)
Vậy theo như những gì bạn vừa nói, nếu biến thuộc kiểu Byte, Integer thì ta nên Dim nó là Long tất tần tật sao? Vì tôi vẩn chưa hiểu ở chổ: Nếu đúng như bạn nói thì kiểu Byte và Integer là dư thừa sao?
Đang tập tành nên còn rất nhiều thứ tôi chưa nắm rõ... Rất mong các bạn hướng dẩn chi tiết

Dư thừa thì ko dư thừa, nhưng tôi đọc rất nhiều về lịch sử VB, lịch sử các ngôn ngữ phát triển của M$ (và có thời suốt ngày học thi mấy mốn MDSE của M$). Tôi cũng ko hề nói là ko tiết kiệm bộ nhớ nhưng biết tiết kiệm ở đâu (chứ ko phải cái gì cũng tiết kiệm) và trong nhiều trường hợp, việc "tiết kiệm bộ nhớ" nên đặt thứ tự ưu tiên thấp hơn là vấn đề "tốc độ". Đó là kinh nghiệm và sự tham khảo các tài liệu dạng software optimizing lâu năm của tôi (viết code ko chỉ để chạy được mà còn phải khoa học và "đẹp" trên mọi phương diện), nhiều "cao thủ" viết soft giao diện đẹp vô thiên lủng nhưng khi nhìn code thì ... thôi rồi lượm ơi (tôi chỉ muốn Ctrl A + Shift Del luôn). Trên rừng dưới biển có rất nhiều cao thủ nhưng cũng có nhiều khái niệm về cao thủ đó lắm, và may mắn thay tôi ko nằm trong nhóm cao thủ đó (vì tôi ghét nhất cái từ đó). Tôi gặp nhiều "cao thủ" dạng đó lắm rồi --=0

Tương tự, chỉ có 5 cái buttons trên thôi nhưng mặc dù khai báo hằng số làm tăng bộ nhớ khi chạy, tôi (và hàng trăm nghìn người lập trình khác trên thế giới) vẫn cứ phải khai báo kiểu như sau để quản lý Button Index (chứ ko dùng số 0, 1, 2, 3...):

Mã:
'// Button Constants
Const CST_BUTTON_NEW = 0
Const CST_BUTTON_SAVE = 1
Const CST_BUTTON_PRINT = 2
Const CST_BUTTON_OTHER = 3
Const CST_BUTTON_CLOSE = 4

Như tôi đã nói ở trên, lý do chính là khi VB6 compiled sang mã máy thì nó sẽ convert những biến dạng Byte, Integer sang kiểu giống như Long lưu trữ. Chính vì thế khi làm việc với những biến kiểu Long thì có tốc độ nhanh nhất trong đám biến dạng số (kiểu currency là chậm nhất). Tôi cũng ko nói là ko tiết kiệm bộ nhớ nhưng nếu sắp xếp theo thứ tự ưu tiên thì tôi đặt tốc độ lên hàng đầu rồi mới đến bộ nhớ. Và như đã giải thích ở trên, chả có gì ghê ghớm về chuyện bộ nhớ cả khi sau thủ tục chứa cái biến đó kết thúc thì bộ nhớ tự động được giải phóng.

Ngay cả tôi biết chắc là for i = 1 to 10 nhưng tôi vẫn khai báo i là kiểu Long.

Bạn hãy nhìn lại các hàm APIs chuẩn của Windows đi, xem tại sao giá trị trở về lúc nào cũng là kiểu Long trong khi chỉ trở về giá trị 0 hoặc -1 (mà đáng ra họ chỉ cần khai báo kiểu Boolean). Đó chính là lý do họ (Microsofter chính hiệu - họ tuy có giỏi nhưng tôi cũng chỉ gọi họ là nhân viên M$ bình thường chứ chả bao giờ gọi họ là cao thủ vì mấy cái coding đó cả) sử dụng biến kiểu Long thay vì kiểu khác. Nếu bạn viết C thì chắc cũng thấy chuyện đó rất bình thường.

Mã:
Public Declare Function GetProcAddress Lib "kernel32" (ByVal hModule As Long, ByVal lpProcName As String) As Long
Public Declare Function LockWindowUpdate Lib "user32" (ByVal hwndLock As Long) As Long
Public Declare Function GetDesktopWindow Lib "user32" () As Long

Nói tóm lại, bạn có thể nghe theo các cao thủ võ lâm mà bạn từng nghe họ. Ý kiến của tôi chỉ vui vui mà thôi. Mà tôi thích kiểu think out of the box.

P/S: Just google (My Mr Cao thủ): Using Long instead of Integer
 
Lần chỉnh sửa cuối:
Upvote 0
Dư thừa thì ko dư thừa, nhưng tôi đọc rất nhiều về lịch sử VB, lịch sử các ngôn ngữ phát triển của M$ (và có thời suốt ngày học thi mấy mốn MDSE của M$). Tôi cũng ko hề nói là ko tiết kiệm bộ nhớ nhưng biết tiết kiệm ở đâu (chứ ko phải cái gì cũng tiết kiệm) và trong nhiều trường hợp, việc "tiết kiệm bộ nhớ" nên đặt thứ tự ưu tiên thấp hơn là vấn đề "tốc độ". Đó là kinh nghiệm và sự tham khảo các tài liệu dạng software optimizing lâu năm của tôi (viết code ko chỉ để chạy được mà còn phải khoa học và "đẹp" trên mọi phương diện)
Dạ vâng! thưa sư phụ! Tôi cũng đang hướng mình đến cái đẹp mà sư phụ vừa nói: Đẹp toàn diện (chứ không phải chỉ có giao diện)
Chính vì lẽ đó mà tôi đang từng bước tìm tòi học hỏi, rút ra kinh nghiệm riêng cho bản thân mình nên rất cần sự trợ giúp của tất cả các cao thủ
nhiều "cao thủ" viết soft giao diện đẹp vô thiên lủng nhưng khi nhìn code thì ... thôi rồi lượm ơi (tôi chỉ muốn Ctrl A + Shift Del luôn). Trên rừng dưới biển có rất nhiều cao thủ nhưng cũng có nhiều khái niệm về cao thủ đó lắm, và may mắn thay tôi ko nằm trong nhóm cao thủ đó (vì tôi ghét nhất cái từ đó). Tôi gặp nhiều "cao thủ" dạng đó lắm rồ
Tôi luôn xem tất cả các thành viên trên diển đàn này đều là những cao thủ vì tôi hỏng có ki lô gì so với họ, và dù rằng cao thủ nhưng đôi khi vẩn phạm sai lầm cũng là chuyện bình thường mà, sự phụ đồng ý chứ
Dù sư phụ không thích nhưng tôi vẩn xem sư phụ là 1 trong những cao thủ thật sự (cũng như tôi đã chấp nhận gọi 2 tiếng sư phụ với tất cả những ai mà tôi cho là THẦY)
Vậy là tôi đã rõ vấn đề cần hỏi, có thể đóng topic tại đây!
Xin cãm ơn thầy Voda, Mr Okebab, Digita, Hai2hai cùng tất cả mọi người
 
Lần chỉnh sửa cuối:
Upvote 0
Xin nói thêm về việc giải phóng bộ nhớ cho biến

Việc tiết kiệm bộ nhớ đáng quan tâm nhất là biến đối tượng.

Các ví dụ sau được viết trong "Module1"

Ví dụ:

'Đầu module khai báo

Dim MyRange As Range

Sub DoWithRange1
Set MyRange = Range("A1:A10")
...
End Sub

Khi thoát khỏi thủ tục thì MyRange vẫn đang chiếm dụng một phần bộ nhớ.

Ví dụ 2:

Sub DoWithRange2
Dim MyRange2 As Range
Set MyRange2 = Range("A1:A10")
...
'Không có lệnh giải phóng bộ nhớ
End Sub

Khi thoát khỏi "DoWithRange2" thì biến MyRange2 được VB giải phóng khỏi vùng nhớ mà không cần người dùng phải Set MyRange2 = Nothing. Vì MyRange2 nằm trong một thủ tục hoặc một hàm.

Tuy vậy nhưng tôi vẫn luôn viết
Sub DoWithRange2
Dim MyRange2 As Range
Set MyRange2 = Range("A1:A10")
...
Set MyRange2 = Nothing ' Như một thói quen tốt :)
End Sub

Với biến đối tượng nhận đối tượng dạng OleAutomation như là nhận Word, Excel, Powerpnt, Access, AutoCAD,...

Dim MyApp As Word.Application 'Hoặc Dim MyApp As Object)
Set MyApp = CreateObject("Word.Application")

Cho dù MyApp được khai báo ở phạm vi trong một thủ tục hay một module, nếu không dùng đến nữa luôn phải đảm bảo 2 dòng lệnh để giải phóng nó là

MyApp.Quit
Set MyApp = Nothing

Còn việc sẽ khai biến dạng gì?

Như anh Hải nói, nếu các biến nhận số nguyên thì nên khai báo về kiểu Long (vì tốc độ và tránh những lỗi Overflow).

Việc chúng ta vẫn cứ khai báo đúng kiểu trong trường hợp:
+ Tôi muốn (chắc chắn) để máy hỗ trợ quản lý loại và khoảng giá trị của biến. Khi tôi muốn biến chỉ nhập số trong khoảng từ 0- 255 mà không được nằm ngoài khoảng thì khai báo

Dim ExcelColumnIndex As BYTE

Nếu những lệnh trên mà chạy trên Excel 2007 thì trở thành sai vì số cột của Excel 2007 là 16384 chứ không phải 255 (kiểu BYTE).
Vậy, Dim ExcelColumnIndex As LONG là một giải pháp tổng thể.

Nếu
Dim ExcelColumnIndex As LONG
trong chương trình phải kiểm soát - là việc làm tốt trong mọi tình huống!)
If (ExcelColumnIndex>0) And ((ExcelColumnIndex<=255 And ExcelVer<12) Or (ExcelColumnIndex<=16384 And ExcelVer>=12)) Then
'Số cột Excel hợp lệ
Else
'Số cột không hợp lệ
End If
 
Lần chỉnh sửa cuối:
Upvote 0
Chú ý: Các biến object thể hiện các resources của windows thì nên release objects ngay sau khi không dùng ở bất cứ trường hợp nào (Đây là điều hiển nhiên trong lập trình rồi). Eg:

Dim oRS AS ADODB.Recordset

Set oRS = New ADODB.Recordset

....

'//Release Object

CloseRecordset oRS '// Closed an open recordset (if not closed, the connection is still active in MS SQL even oRS is Nothing)
KillObject oRS '// If Not oRS is Nothing Then Set oRS = Nothing
...

Còn vài ba loại biến nhớ dạng Integer, Byte, Long, String, Currency, Double, Single, Variant,... thì chú ý đến tốc độ, ý nghĩa của từng loại (Khi nào thì thực sự dùng đến nó) rồi hẵng chú ý tới giá trị của việc tiết kiệm 1 lượng biến nhớ nhỏ đó (có khi nào bạn dùng tới trên 100 biến long đó trong 1 thủ tục ko?).

Hãy cố gắng Google: VB Code Optimizing gì đó đi. Ngày nào tớ cũng lên PSC đọc mấy cái optimize đó đến thuộc lòng rồi (Có rất nhiều stress test liên quan tới numeric & string variants đó chứng minh về tốc độ của việc sử dụng các loại biến đó, kể cả các hàm có chữ $ như Left$(strTemp, 10) nữa.)
 
Lần chỉnh sửa cuối:
Upvote 0
Status
Không mở trả lời sau này.
Web KT

Bài viết mới nhất

Back
Top Bottom